diff options
70 files changed, 1233 insertions, 135 deletions
diff --git a/.cmake.conf b/.cmake.conf index d931c46b4d..7af6d1fd91 100644 --- a/.cmake.conf +++ b/.cmake.conf @@ -1,4 +1,4 @@ -set(QT_REPO_MODULE_VERSION "6.7.1") +set(QT_REPO_MODULE_VERSION "6.7.2") set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1") set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_LEAN_HEADERS=1") diff --git a/dependencies.yaml b/dependencies.yaml index a6bf980290..216dc6bacb 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,16 +1,16 @@ dependencies: ../qtbase: - ref: 82b2436d4c9ff41bcf8cd88697fb4d29fd6db525 + ref: c08aed6d477d761363204df455deaf4d765044b9 required: true ../qtimageformats: - ref: f356b4c936d69bd50db0747d7d4fc6d331c3ce9b + ref: 80e9c6bb8c249315a3f09bcfc654771a99c3328a required: false ../qtlanguageserver: - ref: ea386055119351941b8474986c34b2064cd57d83 + ref: cef5ee11b7bd8d7d53e1e6d5ccaaf1860c51032f required: false ../qtshadertools: - ref: 3ed8a54addbe35460545f7f00b0ba824f69fac41 + ref: c650420e3525948bc9ae04a8235bec5109171527 required: false ../qtsvg: - ref: f9c91b7dd29a74106fe88ff6f3bcbfc43de1f779 + ref: 73f98955cea50e581c9e3f1b46397df564bff084 required: false diff --git a/examples/platforms/android/.gitignore b/examples/platforms/android/.gitignore index 59cf3f824f..e1bb0d95b4 100644 --- a/examples/platforms/android/.gitignore +++ b/examples/platforms/android/.gitignore @@ -1,10 +1,10 @@ *.user build/ -qml_in_java_based_android_project/.idea/ -qml_in_java_based_android_project/.gradle/ -qml_in_java_based_android_project/local.properties -qml_in_java_based_android_project/app/libs/ -qml_in_java_based_android_project/app/src/main/res/xml/qtprovider_paths.xml -qml_in_java_based_android_project/app/src/main/res/values/libs.xml -qml_in_java_based_android_project/app/assets/android_rcc_bundle.rcc +**/.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 index 2cff669bdc..7ed54f821d 100644 --- a/examples/platforms/android/CMakeLists.txt +++ b/examples/platforms/android/CMakeLists.txt @@ -3,3 +3,4 @@ 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/src/qml_in_java_based_android_project.qdoc b/examples/platforms/android/doc/src/qml_in_android_studio_projects.qdoc index 177699d1e1..be1fa8b7a2 100644 --- a/examples/platforms/android/doc/src/qml_in_java_based_android_project.qdoc +++ b/examples/platforms/android/doc/src/qml_in_android_studio_projects.qdoc @@ -2,44 +2,73 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \page qml-in-java-based-android-projects-example.html - \title QML in Java-Based Android Projects - \brief Uses a \l {Qt Quick View Android Class}{QtQuickView} to embed a QML component into a Java-based Android project. + \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 project that utilize the \l {Qt Quick View Android Class}{QtQuickView} API. + 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-based - Android applications. + 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: + 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 - Where an instance of \l {Qt Quick View Android Class}{QtQuickView} named - \c m_qmlView is created by giving it the Java 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 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: + 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 imported QML component we first need to implement + 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}: + \l [Qt Quick View Android Class]{public interface StatusChangeListener}{StatusChangeListener}. + + For a Java-based project: \code public class MainActivity extends AppCompatActivity implements @@ -48,21 +77,44 @@ } \endcode - Then, define an override for the \l [Qt Quick View Android Class]{public interface StatusChangeListener}{StatusChangeListener} callback - function \c onStatusChanged(): + 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()}: + 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}, + \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 @@ -73,11 +125,17 @@ methods. The root object of the QML component's background color is set when a click - event of a Android button occurs: + 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 - With the \l [Qt Quick View Android Class]{public void setProperty(String propertyName, Object value)}{QtQuickView.setProperty()} + 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. @@ -94,10 +152,16 @@ declared in the QML component root object. Here we connect a signal listener to the \c onClicked() signal of the - QML component: + 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 @@ -107,8 +171,14 @@ 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_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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew 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 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/src/qml/configure.cmake b/src/qml/configure.cmake index 2d0434643a..9f8cd55f94 100644 --- a/src/qml/configure.cmake +++ b/src/qml/configure.cmake @@ -14,7 +14,7 @@ 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 diff --git a/src/qml/doc/src/external-resources.qdoc b/src/qml/doc/src/external-resources.qdoc index 091df193a5..ac22729d63 100644 --- a/src/qml/doc/src/external-resources.qdoc +++ b/src/qml/doc/src/external-resources.qdoc @@ -69,3 +69,8 @@ \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/qmlintegration/qqmlintegration.h b/src/qmlintegration/qqmlintegration.h index 1e40ceec17..f1a990a79c 100644 --- a/src/qmlintegration/qqmlintegration.h +++ b/src/qmlintegration/qqmlintegration.h @@ -45,12 +45,16 @@ QT_END_NAMESPACE #define QML_ELEMENT \ Q_CLASSINFO("QML.Element", "auto") + #define QML_ANONYMOUS \ Q_CLASSINFO("QML.Element", "anonymous") \ 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> *); \ + QT_WARNING_POP \ inline constexpr void qt_qmlMarker_anonymous() {} #define QML_NAMED_ELEMENT(NAME) \ @@ -61,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) \ @@ -80,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) \ @@ -110,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) \ @@ -119,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) \ @@ -130,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) \ @@ -144,8 +163,11 @@ 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 \ @@ -155,8 +177,11 @@ QT_END_NAMESPACE 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) \ diff --git a/src/quick/doc/qtquick.qdocconf b/src/quick/doc/qtquick.qdocconf index 10615d82d0..c54b2161d3 100644 --- a/src/quick/doc/qtquick.qdocconf +++ b/src/quick/doc/qtquick.qdocconf @@ -96,7 +96,8 @@ imagedirs += images excludefiles += ../util/qquickpropertychanges_p.h examples.fileextensions += "*.qm" \ - "*.java" + "*.java" \ + "*.kt" manifestmeta.thumbnail.names += "QtQuick/QML Dynamic View Ordering Tutorial*" diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp index f662bcbc2a..accf307382 100644 --- a/src/quick/handlers/qquicktaphandler.cpp +++ b/src/quick/handlers/qquicktaphandler.cpp @@ -461,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() diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h index 7b78690572..c1e7d4bd50 100644 --- a/src/quick/handlers/qquicktaphandler_p.h +++ b/src/quick/handlers/qquicktaphandler_p.h @@ -103,6 +103,7 @@ private: 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; diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp index 26d53f6f0d..2cbb26008c 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; @@ -309,7 +300,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); } diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 74562761c1..2d1cbe77ba 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -82,7 +82,6 @@ QQuickTextPrivate::ExtraData::ExtraData() , doc(nullptr) , minimumPixelSize(12) , minimumPointSize(12) - , nbActiveDownloads(0) , maximumLineCount(INT_MAX) , renderTypeQuality(QQuickText::DefaultRenderTypeQuality) , lineHeightValid(false) @@ -357,29 +356,33 @@ void QQuickText::resourceRequestFinished() 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(); } } @@ -1284,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 = 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(); @@ -1374,6 +1375,11 @@ void QQuickTextPrivate::updateDocumentText() rightToLeftText = extra->doc->toPlainText().isRightToLeft(); } +qreal QQuickTextPrivate::devicePixelRatio() const +{ + return (window ? window->effectiveDevicePixelRatio() : qApp->devicePixelRatio()); +} + /*! \qmltype Text \instantiates QQuickText @@ -1898,14 +1904,32 @@ 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)) { + delete image->pix; + image->pix = nullptr; + + needUpdateLayout = true; + } + } + } + + if (needUpdateLayout) + d->updateLayout(); } break; @@ -2752,7 +2776,7 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data 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()); + node->addImage(QRectF(img->pos.x() + dx, img->pos.y() + dy, img->size.width(), img->size.height()), pix->image()); } } } diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h index 7e8e562ead..9153454a2a 100644 --- a/src/quick/items/qquicktext_p_p.h +++ b/src/quick/items/qquicktext_p_p.h @@ -77,7 +77,6 @@ public: QString hoveredLink; int minimumPixelSize; int minimumPointSize; - int nbActiveDownloads; int maximumLineCount; int renderTypeQuality; bool lineHeightValid : 1; @@ -164,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/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp index 1e437a126d..bf7fbbdacc 100644 --- a/src/quick/items/qquicktextnodeengine.cpp +++ b/src/quick/items/qquicktextnodeengine.cpp @@ -660,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(); @@ -673,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); } } diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 93387852b3..6ad86e83c3 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -928,6 +928,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. */ /*! diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp index 5c22585bf4..79ce859653 100644 --- a/src/quick/items/qquickwindowmodule.cpp +++ b/src/quick/items/qquickwindowmodule.cpp @@ -35,7 +35,15 @@ QQuickWindowQmlImpl::QQuickWindowQmlImpl(QQuickWindowQmlImplPrivate &dd, QWindow : QQuickWindow(dd, parent) { connect(this, &QWindow::visibleChanged, this, &QQuickWindowQmlImpl::visibleChanged); - connect(this, &QWindow::visibilityChanged, this, &QQuickWindowQmlImpl::visibilityChanged); + 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); // We shadow the x and y properties, so that we can re-map them in case @@ -110,6 +118,7 @@ void QQuickWindowQmlImpl::setVisibility(Visibility visibility) { Q_D(QQuickWindowQmlImpl); d->visibility = visibility; + d->visibilityExplicitlySet = true; if (d->componentComplete) applyWindowVisibility(); } @@ -190,8 +199,8 @@ void QQuickWindowQmlImpl::applyWindowVisibility() Q_ASSERT(d->componentComplete); - const bool visible = d->visibility == AutomaticVisibility - ? d->visible : d->visibility != Hidden; + const bool visible = d->visibilityExplicitlySet + ? d->visibility != Hidden : d->visible; qCDebug(lcQuickWindow) << "Applying visible" << visible << "for" << this; @@ -234,20 +243,30 @@ void QQuickWindowQmlImpl::applyWindowVisibility() } } - if (d->visibleExplicitlySet && ((d->visibility == Hidden && d->visible) || - (d->visibility > AutomaticVisibility && !d->visible))) { + 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) { + // 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 { + } 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); } } diff --git a/src/quick/items/qquickwindowmodule_p_p.h b/src/quick/items/qquickwindowmodule_p_p.h index 016af9631a..7e3767b18c 100644 --- a/src/quick/items/qquickwindowmodule_p_p.h +++ b/src/quick/items/qquickwindowmodule_p_p.h @@ -30,6 +30,8 @@ public: bool visible = false; bool visibleExplicitlySet = false; QQuickWindow::Visibility visibility = QQuickWindow::AutomaticVisibility; + bool visibilityExplicitlySet = false; + QV4::PersistentValue rootItemMarker; QMetaObject::Connection itemParentWindowChangeListener; diff --git a/src/quick/jar/org/qtproject/qt/android/QtQuickView.qdoc b/src/quick/jar/org/qtproject/qt/android/QtQuickView.qdoc index 11c94fe8d6..26d08e41c4 100644 --- a/src/quick/jar/org/qtproject/qt/android/QtQuickView.qdoc +++ b/src/quick/jar/org/qtproject/qt/android/QtQuickView.qdoc @@ -60,7 +60,7 @@ } \endcode - For a more detailed example, see \l {QML in Java-Based Android Projects}. + For a more detailed example, see \l {QML in Android Studio Projects}. \section1 Constructors diff --git a/src/quick/util/qquickpixmap_p.h b/src/quick/util/qquickpixmap_p.h index ce210321cb..ea6aa93353 100644 --- a/src/quick/util/qquickpixmap_p.h +++ b/src/quick/util/qquickpixmap_p.h @@ -163,6 +163,7 @@ public: 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; diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index 8de79f2009..10cc407c21 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -56,6 +56,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::Literals::StringLiterals; + Q_DECLARE_LOGGING_CATEGORY(lcQsgLeak) #if defined(QT_DEBUG) && QT_CONFIG(thread) @@ -2008,6 +2010,17 @@ bool QQuickPixmap::isCached(const QUrl &url, const QRect &requestRegion, const Q 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) { diff --git a/src/quickcontrols/basic/ComboBox.qml b/src/quickcontrols/basic/ComboBox.qml index 5c71a4398e..7c272a80e8 100644 --- a/src/quickcontrols/basic/ComboBox.qml +++ b/src/quickcontrols/basic/ComboBox.qml @@ -85,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 @@ -98,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/fusion/ComboBox.qml b/src/quickcontrols/fusion/ComboBox.qml index 609f294d6f..55d91e65ed 100644 --- a/src/quickcontrols/fusion/ComboBox.qml +++ b/src/quickcontrols/fusion/ComboBox.qml @@ -116,6 +116,7 @@ T.ComboBox { topMargin: 6 bottomMargin: 6 padding: 1 + palette: control.palette contentItem: ListView { clip: true @@ -129,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/quicklayouts/qquicklinearlayout.cpp b/src/quicklayouts/qquicklinearlayout.cpp index e10725da1c..fc2a7ffd3e 100644 --- a/src/quicklayouts/qquicklinearlayout.cpp +++ b/src/quicklayouts/qquicklinearlayout.cpp @@ -441,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; diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 01f437fe30..c782dc74b1 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -200,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 @@ -607,41 +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); -#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. - */ - setAttribute(Qt::WA_AcceptTouchEvents); -#endif 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) { @@ -649,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); } diff --git a/src/quickwidgets/qquickwidget_p.h b/src/quickwidgets/qquickwidget_p.h index b5eb7b4ba5..181fbd8d92 100644 --- a/src/quickwidgets/qquickwidget_p.h +++ b/src/quickwidgets/qquickwidget_p.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/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml index 03c186fdb4..d31a0b0fb8 100644 --- a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml +++ b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml @@ -1579,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 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/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index adfc2668a5..97ae290e40 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -549,6 +549,8 @@ private slots: void visibleVsVisibility_data(); void visibleVsVisibility(); + void visibilityDoesntClobberWindowState(); + void eventTypes(); private: @@ -4139,6 +4141,40 @@ void tst_qquickwindow::visibleVsVisibility() 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; 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/tst_palette.cpp b/tests/auto/quickcontrols/palette/tst_palette.cpp index 5109ad0f8f..75ee023bb7 100644 --- a/tests/auto/quickcontrols/palette/tst_palette.cpp +++ b/tests/auto/quickcontrols/palette/tst_palette.cpp @@ -7,9 +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> @@ -19,6 +21,7 @@ #include <QtQuickControls2/qquickstyle.h> #include <QSignalSpy> +using namespace QQuickVisualTestUtils; using namespace QQuickControlsTestUtils; class tst_palette : public QQmlDataTest @@ -52,6 +55,9 @@ private slots: void resetColor(); void updateBindingPalette(); + + void comboBoxPopup_data(); + void comboBoxPopup(); }; tst_palette::tst_palette() @@ -551,6 +557,61 @@ void tst_palette::updateBindingPalette() 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/qquickapplicationwindow/tst_qquickapplicationwindow.cpp b/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp index ff0b4418d2..1d25ffeed6 100644 --- a/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp +++ b/tests/auto/quickcontrols/qquickapplicationwindow/tst_qquickapplicationwindow.cpp @@ -278,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")); 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 } } |