aboutsummaryrefslogtreecommitdiffstats
path: root/tests/manual
diff options
context:
space:
mode:
Diffstat (limited to 'tests/manual')
-rw-r--r--tests/manual/painterpathquickshape/ControlPanel.qml14
-rw-r--r--tests/manual/painterpathquickshape/ControlledShape.qml1
-rw-r--r--tests/manual/platforms/android/qml_in_android_service/.gitignore2
-rw-r--r--tests/manual/platforms/android/qml_in_android_service/CMakeLists.txt33
-rw-r--r--tests/manual/platforms/android/qml_in_android_service/Main.qml100
-rw-r--r--tests/manual/platforms/android/qml_in_android_service/main.cpp10
-rw-r--r--tests/manual/platforms/android/qml_in_java_based_android_project/.gitignore33
-rw-r--r--tests/manual/platforms/android/qml_in_java_based_android_project/README.md35
-rw-r--r--tests/manual/platforms/android/qml_in_java_based_android_project/app/build.gradle83
-rw-r--r--tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/AndroidManifest.xml32
-rw-r--r--tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java20
-rw-r--r--tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/QmlService.java204
-rw-r--r--tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/layout/view_main.xml142
-rw-r--r--tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/strings.xml12
-rw-r--r--tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/backup_rules.xml10
-rw-r--r--tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/data_extraction_rules.xml10
-rw-r--r--tests/manual/platforms/android/qml_in_java_based_android_project/build.gradle4
-rw-r--r--tests/manual/platforms/android/qml_in_java_based_android_project/gradle.properties22
-rw-r--r--tests/manual/platforms/android/qml_in_java_based_android_project/gradle/wrapper/gradle-wrapper.properties7
-rw-r--r--tests/manual/platforms/android/qml_in_java_based_android_project/settings.gradle23
-rw-r--r--tests/manual/quickcontrols/CMakeLists.txt1
-rw-r--r--tests/manual/quickcontrols/menus/CMakeLists.txt47
-rw-r--r--tests/manual/quickcontrols/menus/Main.qml453
-rw-r--r--tests/manual/quickcontrols/menus/Menu.qml6
-rw-r--r--tests/manual/quickcontrols/menus/cppsettings.cpp43
-rw-r--r--tests/manual/quickcontrols/menus/cppsettings.h38
-rw-r--r--tests/manual/quickcontrols/menus/icons/warning.pngbin0 -> 1212 bytes
-rw-r--r--tests/manual/quickcontrols/menus/icons/warning@2x.pngbin0 -> 2118 bytes
-rw-r--r--tests/manual/quickcontrols/menus/main.cpp27
-rw-r--r--tests/manual/svg/data/image/1.svg3
-rw-r--r--tests/manual/svg/data/image/2.svg3
-rw-r--r--tests/manual/svg/data/image/3.svg6
-rw-r--r--tests/manual/svg/data/image/data.pngbin0 -> 6901 bytes
-rw-r--r--tests/manual/svg/data/image/qtlogo.pngbin0 -> 6901 bytes
-rw-r--r--tests/manual/tableview/abstracttablemodel/main.qml36
35 files changed, 1458 insertions, 2 deletions
diff --git a/tests/manual/painterpathquickshape/ControlPanel.qml b/tests/manual/painterpathquickshape/ControlPanel.qml
index 87eb9ae3e7..ea3168d124 100644
--- a/tests/manual/painterpathquickshape/ControlPanel.qml
+++ b/tests/manual/painterpathquickshape/ControlPanel.qml
@@ -28,6 +28,7 @@ Item {
property alias painterComparisonAlpha: painterComparisonColorAlpha.value
property alias outlineEnabled: enableOutline.checked
property alias gradientType: gradientType.currentIndex
+ property alias fillScaleX: fillTransformSlider.value
property alias rendererName: rendererLabel.text
property alias preferCurve: rendererLabel.preferCurve
@@ -254,6 +255,19 @@ Item {
}
}
Label {
+ text: "Fill transform (scale x: " + fillTransformSlider.value.toFixed(2) + "):"
+ color: "white"
+ visible: gradientType.currentIndex != 0
+ }
+ Slider {
+ id: fillTransformSlider
+ Layout.fillWidth: true
+ from: 0.2
+ to: 5.0
+ value: 1.0
+ visible: gradientType.currentIndex != 0
+ }
+ Label {
text: "Fill alpha(" + Math.round(alphaSlider.value*100)/100 + "):"
color: "white"
}
diff --git a/tests/manual/painterpathquickshape/ControlledShape.qml b/tests/manual/painterpathquickshape/ControlledShape.qml
index e690f59ccc..26a57163cd 100644
--- a/tests/manual/painterpathquickshape/ControlledShape.qml
+++ b/tests/manual/painterpathquickshape/ControlledShape.qml
@@ -89,6 +89,7 @@ Item {
strokeStyle: controlPanel.outlineStyle
joinStyle: controlPanel.joinStyle
capStyle: controlPanel.capStyle
+ fillTransform: Qt.matrix4x4(controlPanel.fillScaleX,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1)
}
Repeater {
diff --git a/tests/manual/platforms/android/qml_in_android_service/.gitignore b/tests/manual/platforms/android/qml_in_android_service/.gitignore
new file mode 100644
index 0000000000..2300a2df35
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_android_service/.gitignore
@@ -0,0 +1,2 @@
+build/*
+CMakeLists.txt.user
diff --git a/tests/manual/platforms/android/qml_in_android_service/CMakeLists.txt b/tests/manual/platforms/android/qml_in_android_service/CMakeLists.txt
new file mode 100644
index 0000000000..47afd4c0c3
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_android_service/CMakeLists.txt
@@ -0,0 +1,33 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+cmake_minimum_required(VERSION 3.16)
+
+project(qml_in_android_service VERSION 0.1 LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(Qt6 6.7 REQUIRED COMPONENTS Quick)
+
+qt_standard_project_setup(REQUIRES 6.6)
+
+
+qt_add_executable(qml_in_android_service
+ main.cpp
+)
+
+qt_add_qml_module(qml_in_android_service
+ URI qml_in_android_service
+ VERSION 1.0
+ QML_FILES Main.qml
+)
+
+target_link_libraries(qml_in_android_service
+ PRIVATE Qt6::Quick
+)
+
+install(TARGETS qml_in_android_service
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
diff --git a/tests/manual/platforms/android/qml_in_android_service/Main.qml b/tests/manual/platforms/android/qml_in_android_service/Main.qml
new file mode 100644
index 0000000000..6b8684e525
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_android_service/Main.qml
@@ -0,0 +1,100 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+import QtQuick
+import QtQuick.Controls
+
+Rectangle {
+ id: mainRectangle
+
+ property string colorStringFormat: "#1CB669"
+
+ signal onClicked()
+
+ color: colorStringFormat
+
+ Text {
+ id: helloText
+
+ text: "QML"
+ color: "white"
+ font.pixelSize: 72
+ font.bold: true
+ fontSizeMode: Text.VerticalFit
+ horizontalAlignment: Text.AlignHCenter
+
+ // Height is calculated based on display orientation
+ // from Screen height, dividing numbers are based on what what seem
+ // to look good on most displays
+ height: Screen.width > Screen.height ? Screen.height / 8 : (Screen.height / 2) / 8
+
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ top: parent.top
+ topMargin: 5
+ }
+ }
+
+
+ Text {
+ id: changeColorText
+
+ text: "Tap button to change Java view background color"
+ color: "white"
+ font.pixelSize: 58
+ fontSizeMode: Text.Fit
+ wrapMode: Text.Wrap
+ horizontalAlignment: Text.AlignHCenter
+
+ // Height and width are calculated based on display orientation
+ // from Screen height and width, dividing numbers are based on what seem to
+ // look good on most displays
+ height: Screen.width > Screen.height ? Screen.height / 8 : (Screen.height / 2) / 8
+ width: Screen.width > Screen.height ? (Screen.width / 2) / 2 : Screen.width / 2
+
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ top: helloText.bottom
+ topMargin: Screen.height / 10
+ }
+ }
+
+ Button {
+ id: button
+ // Width is calculated from changeColorText which is calculated from Screen size
+ // dividing numbers are base on what seems to look good on most displays
+ width: changeColorText.width / 1.6
+ height: changeColorText.height * 1.2
+
+ anchors {
+
+ horizontalCenter: parent.horizontalCenter
+ top: changeColorText.bottom
+ topMargin: height / 5
+ }
+
+ onClicked: mainRectangle.onClicked()
+
+ background: Rectangle {
+ id: buttonBackground
+
+ radius: 14
+ color: "#6200EE"
+ opacity: button.down ? 0.6 : 1
+ scale: button.down ? 0.9 : 1
+ }
+
+ contentItem: Text {
+ id: buttonText
+
+ text: "CHANGE COLOR"
+ color: "white"
+ font.pixelSize: 58
+ minimumPixelSize: 10
+ fontSizeMode: Text.Fit
+ font.bold: true
+ wrapMode: Text.Wrap
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+}
diff --git a/tests/manual/platforms/android/qml_in_android_service/main.cpp b/tests/manual/platforms/android/qml_in_android_service/main.cpp
new file mode 100644
index 0000000000..3293373061
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_android_service/main.cpp
@@ -0,0 +1,10 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+#include <QGuiApplication>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ return app.exec();
+}
diff --git a/tests/manual/platforms/android/qml_in_java_based_android_project/.gitignore b/tests/manual/platforms/android/qml_in_java_based_android_project/.gitignore
new file mode 100644
index 0000000000..347e252ef1
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_java_based_android_project/.gitignore
@@ -0,0 +1,33 @@
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Log/OS Files
+*.log
+
+# Android Studio generated files and folders
+captures/
+.externalNativeBuild/
+.cxx/
+*.apk
+output.json
+
+# IntelliJ
+*.iml
+.idea/
+misc.xml
+deploymentTargetDropDown.xml
+render.experimental.xml
+
+# Keystore files
+*.jks
+*.keystore
+
+# Google Services (e.g. APIs or Firebase)
+google-services.json
+
+# Android Profiling
+*.hprof
diff --git a/tests/manual/platforms/android/qml_in_java_based_android_project/README.md b/tests/manual/platforms/android/qml_in_java_based_android_project/README.md
new file mode 100644
index 0000000000..5199430bc8
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_java_based_android_project/README.md
@@ -0,0 +1,35 @@
+# What is this?
+
+This project is for manual testing of embedding QML into Android Services. It
+loads a QML view and a regular Android view side by side, both hosted by a
+Service, and wires them together.
+
+This application is meant to be built using Android Studio, with the Qt Gradle
+plugin. There is no need to manually build the Qt project or edit it, only this
+Android project.
+
+# How to sign the application
+In order to sign the application, you must have a keystore file and list it in
+a 'keystore.properties' file in the project root.
+
+1) Create 'keystore.properties' file in the same folder as this README
+2) Add the following information to the file:
+ ```
+ storePassword=somePassword
+ keyPassword=someOtherPassword
+ keyAlias=someKeyAlias
+ storeFile=/full/path/to/your/keystore.keystore
+ ```
+
+After this, the app build.gradle will read that file and extract the required
+information from it, and use that to sign the app before it is deployed.
+
+# How to configure QtBuild Gradle plugin
+The app-level build.gradle already includes and configures the plugin, but it requires some information about the environment it's running in: The Qt installation directory, and the Qt for Android kit directory.
+
+1) Create 'qtbuild.properties' file in the same folder as this README
+2) Add the following information to the file:
+ ```
+ qtKitDir=/path/to/your/android/kit/
+ qtPath=/path/to/your/Qt/installation // e.g. /etc/Qt/
+ ```
diff --git a/tests/manual/platforms/android/qml_in_java_based_android_project/app/build.gradle b/tests/manual/platforms/android/qml_in_java_based_android_project/app/build.gradle
new file mode 100644
index 0000000000..3aa396c87a
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_java_based_android_project/app/build.gradle
@@ -0,0 +1,83 @@
+plugins {
+ id 'com.android.application'
+ id 'org.qtproject.qt.gradleplugin' version '0.1-SNAPSHOT+'
+}
+
+def qtBuildPropertiesFile = rootProject.file('qtbuild.properties');
+def qtBuildProperties = new Properties();
+qtBuildProperties.load(new FileInputStream(qtBuildPropertiesFile));
+QtBuild {
+ projectPath file('../../qml_in_android_service')
+ qtKitDir file(qtBuildProperties['qtKitDir'])
+ qtPath file(qtBuildProperties['qtPath'])
+}
+
+def keystorePropertiesFile = rootProject.file('keystore.properties');
+def keystoreProperties = new Properties();
+keystoreProperties.load(new FileInputStream(keystorePropertiesFile));
+
+android {
+ signingConfigs {
+ debug {
+ storeFile file(keystoreProperties['storeFile'])
+ storePassword keystoreProperties['storePassword']
+ keyAlias keystoreProperties['keyAlias']
+ keyPassword keystoreProperties['keyPassword']
+ }
+ }
+ namespace 'com.example.qml_in_java_based_android_project'
+ compileSdk 34
+
+ defaultConfig {
+ applicationId "com.example.qml_in_java_based_android_project"
+ minSdk 28
+ targetSdk 34
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ signingConfig signingConfigs.debug
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ signingConfig signingConfigs.debug
+ }
+ debug {
+ signingConfig signingConfigs.debug
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ packagingOptions {
+ jniLibs {
+ useLegacyPackaging true
+ }
+ }
+ sourceSets {
+ main {
+ assets {
+ srcDirs 'assets'
+ }
+ jniLibs {
+ srcDirs 'libs'
+ }
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.appcompat:appcompat:1.6.1'
+ implementation 'com.google.android.material:material:1.9.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ implementation "org.qtproject.qt.gradleplugin:QtGradlePlugin:0.1-SNAPSHOT"
+ 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/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/AndroidManifest.xml b/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..896b975a0b
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+
+ <application
+ android:allowBackup="true"
+ android:dataExtractionRules="@xml/data_extraction_rules"
+ android:fullBackupContent="@xml/backup_rules"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ tools:targetApi="34"
+ android:theme="@style/Theme.AppCompat">
+ <service
+ android:name=".QmlService"
+ android:enabled="true"
+ android:exported="true"/>
+
+ <activity
+ android:name=".MainActivity"
+ android:configChanges="orientation|screenLayout|screenSize"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java b/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java
new file mode 100644
index 0000000000..c619dce985
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/MainActivity.java
@@ -0,0 +1,20 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+package com.example.qml_in_java_based_android_project;
+
+import androidx.appcompat.app.AppCompatActivity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class MainActivity extends AppCompatActivity
+{
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.view_main);
+ startService(new Intent(this, QmlService.class));
+ finish();
+ }
+}
diff --git a/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/QmlService.java b/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/QmlService.java
new file mode 100644
index 0000000000..0c9de067e6
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/java/com/example/qml_in_java_based_android_project/QmlService.java
@@ -0,0 +1,204 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+package com.example.qml_in_java_based_android_project;
+
+import android.annotation.SuppressLint;
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.util.Size;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.Gravity;
+
+import android.widget.Button;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import java.util.Random;
+import java.util.function.Consumer;
+
+import org.qtproject.qt.android.QtQuickView;
+import org.qtproject.qt.android.QtQmlStatus;
+import org.qtproject.qt.android.QtQmlStatusChangeListener;
+import org.qtproject.example.qml_in_android_service.Qml_in_android_service.Main;
+
+@SuppressLint("UseSwitchCompatOrMaterialCode")
+public class QmlService extends Service implements QtQmlStatusChangeListener
+{
+ private static final String TAG = "QmlService";
+ private WindowManager m_windowManager;
+ private QtQuickView m_serviceView;
+ private final Main m_serviceViewComponent = new Main();
+ private View m_mainView;
+
+ private TextView m_qmlBackgroundColorTextView;
+ private TextView m_qmlStatusTextView;
+ private View m_colorBox;
+ private Switch m_connectionSwitch;
+ private int m_qmlSignalListenerId;
+
+ @Override
+ public IBinder onBind(Intent intent)
+ {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ @Override
+ public void onCreate()
+ {
+ m_windowManager = getSystemService(WindowManager.class);
+
+ getScreenSize((size) -> {
+ // Get the available geometry, and split it between the Android and QML UIs
+ m_serviceView = addQuickView(new Size(size.getWidth() / 2, size.getHeight()));
+ m_serviceViewComponent.setStatusChangeListener(this);
+ m_serviceView.loadComponent(m_serviceViewComponent);
+
+ m_mainView = addMainView(new Size(size.getWidth() / 2, size.getHeight()));
+ connectToNativeControls(m_mainView);
+ });
+ }
+
+ /*
+ Draw the "main" view on the left side of the screen, with the native controls
+ */
+ private View addMainView(final Size size)
+ {
+ final LayoutInflater inflater = getSystemService(LayoutInflater.class);
+
+ final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
+ size.getWidth(), size.getHeight(),
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+ WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ & ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ layoutParams.gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
+
+ View mainView = inflater.inflate(R.layout.view_main, null);
+ m_windowManager.addView(mainView, layoutParams);
+ return mainView;
+ }
+
+ /*
+ Take size, and draw QtQuickView of that size on the right side of the screen
+ */
+ private QtQuickView addQuickView(final Size size)
+ {
+ WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
+ size.getWidth(), size.getHeight(),
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+ WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ & ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ layoutParams.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
+
+ QtQuickView serviceView = new QtQuickView(this);
+ m_windowManager.addView(serviceView, layoutParams);
+ return serviceView;
+ }
+
+ /*
+ Draw empty View that fills the parent (screen in this case) to discover the available size,
+ report to consumer
+ */
+ private void getScreenSize(final Consumer<Size> screenSizeConsumer)
+ {
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+ WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ & ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ final View view = new View(this) {
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom)
+ {
+ m_windowManager.removeView(this);
+ screenSizeConsumer.accept(new Size(right - left, bottom - top));
+ }
+ };
+ m_windowManager.addView(view, params);
+ }
+
+ @Override
+ public void onDestroy()
+ {
+ if (m_windowManager != null) {
+ if (m_serviceView != null) {
+ m_windowManager.removeView(m_serviceView);
+ m_serviceView = null;
+ }
+ if (m_mainView != null) {
+ m_windowManager.removeView(m_mainView);
+ m_mainView = null;
+ }
+ }
+ }
+
+ /*
+ Connect listeners to the native controls
+ */
+ private void connectToNativeControls(final View mainView)
+ {
+ m_qmlBackgroundColorTextView = mainView.findViewById(R.id.qmlBackgroundColorText);
+ m_qmlStatusTextView = mainView.findViewById(R.id.qmlStatus);
+ m_colorBox = mainView.findViewById(R.id.box);
+
+ m_connectionSwitch = mainView.findViewById(R.id.switch1);
+ m_connectionSwitch.setOnCheckedChangeListener(
+ (buttonView, isChecked) -> connectSwitchListener(isChecked));
+
+ final Button changeColorButton = mainView.findViewById(R.id.button);
+ changeColorButton.setOnClickListener(this::onChangeColorButtonListener);
+ }
+
+ public void onChangeColorButtonListener(View view)
+ {
+ m_serviceViewComponent.setColorStringFormat(getRandomColorString());
+
+ final String qmlColor = m_serviceView.getProperty("colorStringFormat");
+ m_qmlBackgroundColorTextView.setText(qmlColor);
+ m_colorBox.setBackgroundColor(Color.parseColor(qmlColor));
+ }
+
+ @Override
+ public void onStatusChanged(QtQmlStatus status)
+ {
+ m_qmlStatusTextView.setText(
+ String.format("%s %s", getResources().getString(R.string.qml_view_status), status));
+ // Once QML is loaded and the signal listener switch is not checked,
+ // connect to onClicked() signal in main.qml
+ if (status == QtQmlStatus.READY && m_connectionSwitch.isChecked())
+ connectSwitchListener(m_connectionSwitch.isChecked());
+ }
+
+ private void connectSwitchListener(boolean checked)
+ {
+ if (checked) {
+ m_qmlSignalListenerId = m_serviceView.connectSignalListener(
+ "onClicked", Object.class, this::onQmlChangeColorButtonClicked);
+ } else {
+ m_serviceView.disconnectSignalListener(m_qmlSignalListenerId);
+ }
+ }
+
+ public void onQmlChangeColorButtonClicked(String signal, Object o)
+ {
+ m_mainView.setBackgroundColor(Color.parseColor(getRandomColorString()));
+ }
+
+ private String getRandomColorString()
+ {
+ Random rand = new Random();
+ return String.format("#%06x", rand.nextInt(0xffffff + 1));
+ }
+}
diff --git a/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/layout/view_main.xml b/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/layout/view_main.xml
new file mode 100644
index 0000000000..65b6b3fe6c
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/layout/view_main.xml
@@ -0,0 +1,142 @@
+<?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"
+ android:orientation="vertical"
+ android:baselineAligned="false"
+ android:background="#AF93DF">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="8dp"
+ android:gravity="center_horizontal"
+ android:includeFontPadding="false"
+ android:text="@string/java"
+ android:textColor="#FFFFFF"
+ 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="#FFFFFF"/>
+
+ <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="wrap_content"
+ android:layout_height="wrap_content"
+ 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="#FFFFFF" />
+
+ <Button
+ android:id="@+id/button"
+ android:layout_width="wrap_content"
+ 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="wrap_content"
+ android:layout_height="wrap_content"
+ 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/connect_qml_button_signal_listener"
+ android:textColor="#FFFFFF" />
+
+ <Switch
+ android:id="@+id/switch1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:textOff="@string/off"
+ android:textOn="@string/on"
+ android:showText="true"
+ android:checked="true"
+ tools:ignore="UseSwitchCompatOrMaterialXml" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/qmlColorLinear"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="10dp">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ 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="#FFFFFF" />
+
+ <TextView
+ android:id="@+id/qmlBackgroundColorText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal"
+ android:textColor="#FFFFFF" />
+ </LinearLayout>
+
+ <View
+ android:id="@+id/box"
+ android:layout_width="100dp"
+ android:layout_height="50dp"
+ android:layout_gravity="center_horizontal"
+ android:background="@android:color/transparent"
+ android:layout_weight="0"/>
+ </LinearLayout>
+</LinearLayout>
diff --git a/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/strings.xml b/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..39d33f40c9
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/values/strings.xml
@@ -0,0 +1,12 @@
+<resources>
+ <string name="app_name">qml_in_java_based_android_project</string>
+ <string name="button">Change color</string>
+ <string name="java">Java</string>
+ <string name="change_qml_background">Tap button to change QML view background color</string>
+ <string name="connect_qml_button_signal_listener">QML Button listener connected</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/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/backup_rules.xml b/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000000..04dd1acfe3
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample backup rules file; uncomment and customize as necessary.
+ See https://developer.android.com/guide/topics/data/autobackup
+ for details.
+ Note: This file is ignored for devices older that API 31
+ See https://developer.android.com/about/versions/12/backup-restore
+-->
+<full-backup-content>
+</full-backup-content>
+
diff --git a/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/data_extraction_rules.xml b/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000000..9840b57766
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_java_based_android_project/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample data extraction rules file; uncomment and customize as necessary.
+ See https://developer.android.com/about/versions/12/backup-restore#xml-changes
+ for details.
+-->
+<data-extraction-rules>
+ <cloud-backup>
+ </cloud-backup>
+</data-extraction-rules>
+
diff --git a/tests/manual/platforms/android/qml_in_java_based_android_project/build.gradle b/tests/manual/platforms/android/qml_in_java_based_android_project/build.gradle
new file mode 100644
index 0000000000..b92d690313
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_java_based_android_project/build.gradle
@@ -0,0 +1,4 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+id 'com.android.application' version '7.4.1' apply false
+}
diff --git a/tests/manual/platforms/android/qml_in_java_based_android_project/gradle.properties b/tests/manual/platforms/android/qml_in_java_based_android_project/gradle.properties
new file mode 100644
index 0000000000..dacb776f4a
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_java_based_android_project/gradle.properties
@@ -0,0 +1,22 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
+
diff --git a/tests/manual/platforms/android/qml_in_java_based_android_project/gradle/wrapper/gradle-wrapper.properties b/tests/manual/platforms/android/qml_in_java_based_android_project/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000..62f495dfed
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_java_based_android_project/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/tests/manual/platforms/android/qml_in_java_based_android_project/settings.gradle b/tests/manual/platforms/android/qml_in_java_based_android_project/settings.gradle
new file mode 100644
index 0000000000..8a59ffb868
--- /dev/null
+++ b/tests/manual/platforms/android/qml_in_java_based_android_project/settings.gradle
@@ -0,0 +1,23 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ maven {
+ url "https://android.qt.io/maven/snapshots"
+ }
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ maven {
+ url "https://android.qt.io/maven/snapshots"
+ }
+ }
+}
+
+rootProject.name = "qml_in_java_based_android_project"
+include ':app'
diff --git a/tests/manual/quickcontrols/CMakeLists.txt b/tests/manual/quickcontrols/CMakeLists.txt
index e7f07e6110..fa3bf67e9d 100644
--- a/tests/manual/quickcontrols/CMakeLists.txt
+++ b/tests/manual/quickcontrols/CMakeLists.txt
@@ -10,6 +10,7 @@ if(LINUX)
endif()
add_subdirectory(headerview)
add_subdirectory(imagine/musicplayer)
+add_subdirectory(menus)
add_subdirectory(qquickdialog)
add_subdirectory(screenshots)
add_subdirectory(sidepanel)
diff --git a/tests/manual/quickcontrols/menus/CMakeLists.txt b/tests/manual/quickcontrols/menus/CMakeLists.txt
new file mode 100644
index 0000000000..ce757613a1
--- /dev/null
+++ b/tests/manual/quickcontrols/menus/CMakeLists.txt
@@ -0,0 +1,47 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(menus VERSION 0.1 LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+find_package(Qt6 REQUIRED COMPONENTS Quick QuickControls2)
+
+qt_standard_project_setup(REQUIRES 6.8)
+
+qt_add_executable(appmenus
+ main.cpp
+)
+
+qt_add_qml_module(appmenus
+ URI Menus
+ VERSION 1.0
+ QML_FILES
+ Main.qml
+ SOURCES
+ cppsettings.cpp
+ cppsettings.h
+ main.cpp
+ RESOURCES
+ icons/warning.png
+ icons/warning@2x.png
+)
+
+# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
+# If you are developing for iOS or macOS you should consider setting an
+# explicit, fixed bundle identifier manually though.
+set_target_properties(appmenus PROPERTIES
+# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appmenus
+ MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
+ MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
+ MACOSX_BUNDLE TRUE
+ WIN32_EXECUTABLE TRUE
+)
+
+target_link_libraries(appmenus
+ PRIVATE
+ Qt6::Quick
+ Qt6::QuickControls2
+)
diff --git a/tests/manual/quickcontrols/menus/Main.qml b/tests/manual/quickcontrols/menus/Main.qml
new file mode 100644
index 0000000000..a75f2afd6c
--- /dev/null
+++ b/tests/manual/quickcontrols/menus/Main.qml
@@ -0,0 +1,453 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtCore
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+import QtQuick.Dialogs
+
+ApplicationWindow {
+ id: window
+ width: 800
+ height: 600
+ visible: true
+ title: qsTr("Menus - style: %1").arg(currentStyle)
+
+ required property string currentStyle
+
+ Shortcut {
+ sequence: "Ctrl+Q"
+ onActivated: Qt.quit()
+ }
+
+ Settings {
+ id: settings
+
+ property alias windowX: window.x
+ property alias windowY: window.y
+ }
+
+ menuBar: MenuBar {
+ visible: menuBarVisibleSwitch.checked
+
+ Menu {
+ id: fileMenu
+ objectName: "file"
+ title: qsTr("&File")
+ popupType: popupTypeCombo.currentIndex
+ ContextAction { text: qsTr("&New...") }
+ ContextMenuItem { text: "menuItem" }
+ ContextAction { text: qsTr("&Open...") }
+ ContextAction { text: qsTr("&Save") }
+ ContextAction { text: qsTr("Save &As...") }
+ Menu {
+ title: qsTr("Sub...")
+ ContextAction { text: qsTr("Sub action 1") }
+ ContextAction { text: qsTr("Sub action 2") }
+ }
+ MenuSeparator { }
+ ContextAction {
+ text: qsTr("&Quit")
+ // This is needed for macOS since it takes priority over the Shortcut.
+ onTriggered: Qt.quit()
+ }
+ Action {
+ text: qsTr("Remove menu")
+ onTriggered: menuBar.removeMenu(fileMenu)
+ }
+ }
+ Menu {
+ id: editMenu
+ objectName: "edit"
+ title: qsTr("&Edit")
+ popupType: popupTypeCombo.currentIndex
+ ContextAction {
+ id: cutAction
+ text: qsTr("Cut")
+ enabled: textArea.selectedText.length > 0
+ }
+ ContextAction {
+ text: qsTr("Copy")
+ enabled: textArea.selectedText.length > 0
+ }
+ ContextAction {
+ text: qsTr("Paste")
+ enabled: textArea.activeFocus
+ }
+
+ MenuSeparator {}
+
+ Action {
+ text: qsTr("Checkable menu")
+ checkable: true
+ checked: true
+ }
+ Action {
+ text: qsTr("Remove menu")
+ onTriggered: menuBar.removeMenu(editMenu)
+ }
+ Menu {
+ id: editSubMenu
+ title: qsTr("Find / Replace")
+ Action { text: qsTr("&Find") }
+ }
+
+ MenuSeparator {}
+
+ ContextAction {
+ text: qsTr("Dummy Action")
+ shortcut: "Ctrl+I"
+ }
+ }
+ MenuBarItem {
+ id: explicitMenuBarItem
+ menu: Menu {
+ id: menuBarItemMenu
+ objectName: "MenuBarItem"
+ title: "MenuBarItem"
+ popupType: popupTypeCombo.currentIndex
+ ContextAction { text: qsTr("Action") }
+ Action {
+ text: qsTr("Remove menu")
+ onTriggered: menuBar.removeMenu(menuBarItemMenu)
+ }
+ }
+ }
+ }
+
+ Component {
+ id: extraMenuComp
+ Menu {
+ id: extraMenu
+ objectName: "Extra"
+ title: qsTr("&Extra")
+ ContextAction { text: qsTr("&Trigger") }
+ Action {
+ text: qsTr("Remove Extra menu")
+ onTriggered: menuBar.removeMenu(extraMenu)
+ }
+ }
+ }
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ Label {
+ text: qsTr("Right click on the window background to open a context menu. "
+ + "Right click on the TextArea to access its edit context menu.\n\n"
+ + "Things to check:\n\n"
+ + "- Do the menu items trigger their actions (check console for output)?\n"
+ + "- Do checkable menu items work?\n"
+ + "- Do the Edit menu items (in the MenuBar menu and edit context menu)"
+ + " work as expected with the TextArea?\n"
+ + " - Are they enabled/disabled as expected?\n"
+ + " - Does the TextArea keep focus after interacting with the Edit menu items?\n"
+ + "- Does adding and removing menu items work?\n"
+ + "- Do the menus in the MenuBar work?\n"
+ + "- Can you add and remove menus from the MenuBar?\n"
+ + "- Do shortcuts work?")
+ verticalAlignment: Text.AlignVCenter
+ wrapMode: Text.Wrap
+
+ Layout.alignment: Qt.AlignHCenter
+ Layout.preferredWidth: window.width * 0.5
+ Layout.fillHeight: true
+ }
+
+ GroupBox {
+ title: qsTr("Context menu")
+
+ Layout.fillWidth: true
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ RowLayout {
+ Label {
+ text: qsTr("Popup type")
+ }
+
+ ComboBox {
+ id: popupTypeCombo
+ model: ["Default", "Item", "Window", "Native"]
+ onCurrentIndexChanged: CppSettings.popupType = currentIndex
+ currentIndex: CppSettings.popupType
+ }
+ }
+
+ Row {
+ Button {
+ text: qsTr("Add action")
+ onClicked: backgroundContextMenu.appendAction()
+ }
+ Button {
+ text: qsTr("Remove action")
+ onClicked: backgroundContextMenu.removeLastAction()
+ }
+
+ Button {
+ text: qsTr("Add sub-menu action")
+ onClicked: subMenu.appendAction()
+ }
+ Button {
+ text: qsTr("Remove sub-menu action")
+ onClicked: subMenu.removeLastAction()
+ }
+ }
+ }
+ }
+
+ TextArea {
+ id: textArea
+ text: qsTr("Dummy TextArea to test disabled menu items")
+
+ Layout.fillWidth: true
+ Layout.minimumHeight: 100
+
+ TapHandler {
+ objectName: "textAreaMouseTapHandler"
+ acceptedButtons: Qt.RightButton
+ onPressedChanged: if (pressed) editContextMenu.popup()
+ }
+ TapHandler {
+ objectName: "textAreaTouchTapHandler"
+ acceptedDevices: PointerDevice.TouchScreen
+ onLongPressed: editContextMenu.popup()
+ }
+ }
+
+ Component {
+ id: menuBarItemComp
+ MenuBarItem {
+ }
+ }
+
+ MessageDialog {
+ id: restartNeededDialog
+ buttons: MessageDialog.Ok
+ text: "Your current changes requires a restart to take effect!"
+ }
+
+ GroupBox {
+ title: qsTr("MenuBar")
+
+ Layout.fillWidth: true
+
+ ColumnLayout {
+ anchors.fill: parent
+
+ Row {
+ Switch {
+ text: qsTr("Don't use native menu bar")
+ checked: CppSettings.dontUseNativeMenuBar
+
+ onClicked: {
+ CppSettings.dontUseNativeMenuBar = checked
+ restartNeededDialog.open()
+ }
+ }
+ Switch {
+ id: menuBarVisibleSwitch
+ text: qsTr("MenuBar visible")
+ checked: true
+ }
+ }
+ Row {
+ Button {
+ text: "Append menu"
+ onClicked: {
+ let menu = extraMenuComp.createObject(menuBar, { title: "Extra " + menuBar.count })
+ menuBar.addMenu(menu)
+ }
+ }
+ Button {
+ text: "Prepend menu"
+ onClicked: {
+ let menu = extraMenuComp.createObject(menuBar, { title: "Extra " + menuBar.count })
+ menuBar.insertMenu(0, menu)
+ }
+ }
+ Button {
+ text: qsTr("Add file menu")
+ onClicked: menuBar.addMenu(fileMenu)
+ }
+ Button {
+ text: "Change labels"
+ onClicked: {
+ fileMenu.title = "File changed"
+ cutAction.text = "Cut changed"
+ }
+ }
+ Button {
+ text: "toggle delegate"
+ onClicked: menuBar.delegate = menuBar.delegate ? null : menuBarItemComp
+ }
+ Switch {
+ text: "MenuBarItem visible"
+ checked: true
+ onCheckedChanged: explicitMenuBarItem.visible = checked
+ }
+ }
+ }
+ }
+ }
+
+ TapHandler {
+ objectName: "backgroundMouseTapHandler"
+ acceptedButtons: Qt.RightButton
+ onPressedChanged: if (pressed) backgroundContextMenu.popup()
+ }
+ TapHandler {
+ objectName: "backgroundTouchTapHandler"
+ acceptedDevices: PointerDevice.TouchScreen
+ onLongPressed: backgroundContextMenu.popup()
+ }
+
+ Component {
+ id: actionComponent
+
+ Action {}
+ }
+
+ component ContextAction: Action {
+ onCheckedChanged: (checked) => print("checked of \"" + text + "\" changed to " + checked)
+ onTriggered: print("triggered \"" + text + "\"")
+ }
+
+ component ContextMenuItem: MenuItem {
+ onCheckedChanged: print("checked of \"" + text + "\" changed to " + checked)
+ onTriggered: print("triggered \"" + text + "\"")
+ }
+
+ Menu {
+ id: backgroundContextMenu
+ objectName: "backgroundContextMenu"
+ popupType: popupTypeCombo.currentIndex
+
+ function appendAction() {
+ let action = actionComponent.createObject(null, { text: qsTr("Extra context menu item") })
+ backgroundContextMenu.addAction(action)
+ }
+
+ function removeLastAction() {
+ // TODO: Can't use count here because it's 0: it uses contentModel->count(), but native menu items
+ // are not Qt Quick items, so we either need to document that you should use contentData.count
+ // or add an "actions" property. The problem with contentData is that it could contain
+ // non-Action objects. Another potential issue is that "It is not re-ordered when items are inserted or moved",
+ // making it unreliable as a general purpose container of actions if users add or remove them dynamically.
+ backgroundContextMenu.removeAction(backgroundContextMenu.actionAt(backgroundContextMenu.contentData.length - 1))
+ }
+
+ ContextAction {
+ text: qsTr("Context menu item")
+ shortcut: "A"
+ }
+ ContextMenuItem {
+ text: qsTr("Checkable context menu item")
+ checkable: true
+ }
+ ContextAction {
+ text: qsTr("Checked context menu item")
+ checkable: true
+ checked: true
+ shortcut: "C"
+ }
+ ContextAction {
+ text: qsTr("Disabled context menu item")
+ enabled: false
+ shortcut: "D"
+ }
+ ContextAction {
+ text: qsTr("Checked and disabled context menu item")
+ checkable: true
+ checked: true
+ enabled: false
+ shortcut: "E"
+ }
+
+ MenuSeparator {}
+
+ ContextAction {
+ text: qsTr("Context menu item with icon (name)")
+ icon.name: "mail-send"
+ }
+
+ ContextAction {
+ text: qsTr("Context menu item with icon (source)")
+ icon.source: "qrc:/qt/qml/Menus/icons/warning.png"
+ }
+
+ ContextAction {
+ text: qsTr("Context menu item with disabled icon (source)")
+ icon.source: "qrc:/qt/qml/Menus/icons/warning.png"
+ enabled: false
+ }
+
+ MenuSeparator {}
+
+ Menu {
+ id: subMenu
+ title: qsTr("Sub-menu")
+ objectName: title
+ popupType: backgroundContextMenu.popupType
+
+ function appendAction() {
+ let action = actionComponent.createObject(null, { text: qsTr("Extra sub-menu item") })
+ subMenu.addAction(action)
+ }
+
+ function removeLastAction() {
+ subMenu.removeAction(subMenu.actionAt(subMenu.contentData.length - 1))
+ }
+
+ ContextAction {
+ text: qsTr("Sub-menu item")
+ }
+ ContextAction {
+ text: qsTr("Checkable sub-menu item")
+ checkable: true
+ shortcut: "G"
+ }
+ ContextAction {
+ text: qsTr("Checked sub-menu item")
+ checkable: true
+ checked: true
+ }
+
+ MenuSeparator {}
+
+ ContextAction {
+ text: qsTr("Disabled sub-menu item")
+ enabled: false
+ shortcut: "I"
+ }
+ ContextAction {
+ text: qsTr("Checked and disabled sub-menu item")
+ checkable: true
+ checked: true
+ enabled: false
+ shortcut: "J"
+ }
+ }
+ }
+
+ Menu {
+ id: editContextMenu
+ objectName: "editContextMenu"
+
+ ContextAction {
+ text: qsTr("Cut")
+ enabled: textArea.selectedText.length > 0
+ }
+ ContextAction {
+ text: qsTr("Copy")
+ enabled: textArea.selectedText.length > 0
+ }
+ ContextAction {
+ text: qsTr("Paste")
+ enabled: textArea.activeFocus
+ }
+ }
+}
+
diff --git a/tests/manual/quickcontrols/menus/Menu.qml b/tests/manual/quickcontrols/menus/Menu.qml
new file mode 100644
index 0000000000..0d18fca2ab
--- /dev/null
+++ b/tests/manual/quickcontrols/menus/Menu.qml
@@ -0,0 +1,6 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick.Controls
+
+Menu {}
diff --git a/tests/manual/quickcontrols/menus/cppsettings.cpp b/tests/manual/quickcontrols/menus/cppsettings.cpp
new file mode 100644
index 0000000000..589cea916b
--- /dev/null
+++ b/tests/manual/quickcontrols/menus/cppsettings.cpp
@@ -0,0 +1,43 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "cppsettings.h"
+
+#include <QCoreApplication>
+
+CppSettings::CppSettings(QObject *parent) :
+ QObject(parent),
+ mSettings("QtProject", "menus")
+{
+ QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, dontUseNativeMenuBar());
+}
+
+bool CppSettings::dontUseNativeMenuBar() const
+{
+ return mSettings.value("dontUseNativeMenuBar").toBool();
+}
+
+void CppSettings::setDontUseNativeMenuBar(bool dontUseNativeMenuBar)
+{
+ const bool oldValue = this->dontUseNativeMenuBar();
+ if (dontUseNativeMenuBar == oldValue)
+ return;
+
+ QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, dontUseNativeMenuBar);
+ mSettings.setValue("dontUseNativeMenuBar", dontUseNativeMenuBar);
+ emit dontUseNativeMenuBarChanged();
+}
+
+int CppSettings::popupType() const
+{
+ return mSettings.value("popupType").toInt();
+}
+
+void CppSettings::setPopupType(int newPopupType)
+{
+ const int oldValue = popupType();
+ if (oldValue == newPopupType)
+ return;
+ mSettings.setValue("popupType", newPopupType);
+ emit popupTypeChanged();
+}
diff --git a/tests/manual/quickcontrols/menus/cppsettings.h b/tests/manual/quickcontrols/menus/cppsettings.h
new file mode 100644
index 0000000000..b6af1f9f09
--- /dev/null
+++ b/tests/manual/quickcontrols/menus/cppsettings.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef CPPSETTINGS_H
+#define CPPSETTINGS_H
+
+#include <QObject>
+#include <QQmlEngine>
+#include <QSettings>
+
+class CppSettings : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool dontUseNativeMenuBar READ dontUseNativeMenuBar WRITE setDontUseNativeMenuBar
+ NOTIFY dontUseNativeMenuBarChanged FINAL)
+ Q_PROPERTY(int popupType READ popupType WRITE setPopupType
+ NOTIFY popupTypeChanged FINAL)
+ QML_ELEMENT
+ QML_SINGLETON
+
+public:
+ explicit CppSettings(QObject *parent = nullptr);
+
+ bool dontUseNativeMenuBar() const;
+ void setDontUseNativeMenuBar(bool dontUseNativeMenuBar);
+
+ int popupType() const;
+ void setPopupType(int newPopupType);
+
+signals:
+ void dontUseNativeMenuBarChanged();
+ void popupTypeChanged();
+
+private:
+ QSettings mSettings;
+};
+
+#endif // CPPSETTINGS_H
diff --git a/tests/manual/quickcontrols/menus/icons/warning.png b/tests/manual/quickcontrols/menus/icons/warning.png
new file mode 100644
index 0000000000..590a61eb80
--- /dev/null
+++ b/tests/manual/quickcontrols/menus/icons/warning.png
Binary files differ
diff --git a/tests/manual/quickcontrols/menus/icons/warning@2x.png b/tests/manual/quickcontrols/menus/icons/warning@2x.png
new file mode 100644
index 0000000000..487fbafcfd
--- /dev/null
+++ b/tests/manual/quickcontrols/menus/icons/warning@2x.png
Binary files differ
diff --git a/tests/manual/quickcontrols/menus/main.cpp b/tests/manual/quickcontrols/menus/main.cpp
new file mode 100644
index 0000000000..e9b4e6d5eb
--- /dev/null
+++ b/tests/manual/quickcontrols/menus/main.cpp
@@ -0,0 +1,27 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+#include <QQuickStyle>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication::setOrganizationName("QtProject");
+ QGuiApplication::setApplicationName("menus");
+
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.setInitialProperties({{ "currentStyle", QQuickStyle::name() }});
+ QObject::connect(
+ &engine,
+ &QQmlApplicationEngine::objectCreationFailed,
+ &app,
+ []() { QCoreApplication::exit(-1); },
+ Qt::QueuedConnection);
+ engine.loadFromModule("Menus", "Main");
+
+ return app.exec();
+}
+
diff --git a/tests/manual/svg/data/image/1.svg b/tests/manual/svg/data/image/1.svg
new file mode 100644
index 0000000000..d5f27450c2
--- /dev/null
+++ b/tests/manual/svg/data/image/1.svg
@@ -0,0 +1,3 @@
+<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
+ <image xlink:href="data.png" height="200" width="200" />
+</svg>
diff --git a/tests/manual/svg/data/image/2.svg b/tests/manual/svg/data/image/2.svg
new file mode 100644
index 0000000000..3f71e5ca23
--- /dev/null
+++ b/tests/manual/svg/data/image/2.svg
@@ -0,0 +1,3 @@
+<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
+ <image xlink:href="qtlogo.png" height="200" width="200" />
+</svg>
diff --git a/tests/manual/svg/data/image/3.svg b/tests/manual/svg/data/image/3.svg
new file mode 100644
index 0000000000..85751587d9
--- /dev/null
+++ b/tests/manual/svg/data/image/3.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg viewBox="0 0 210 297">
+<image width="200" height="200" preserveAspectRatio="none" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFQAAAA8CAYAAAGPy61gAAAAAXNSR0IArs4c6QAAAJZlWElmTU0A&#10;KgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgExAAIAAAARAAAAWodp&#10;AAQAAAABAAAAbAAAAAAAAABIAAAAAQAAAEgAAAABQWRvYmUgSW1hZ2VSZWFkeQAAAAOgAQADAAAA&#10;AQABAACgAgAEAAAAAQAAAFSgAwAEAAAAAQAAADwAAAAAm/SbDAAAAAlwSFlzAAALEwAACxMBAJqc&#10;GAAAAi1pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6&#10;bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDYuMC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRm&#10;PSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJk&#10;ZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8v&#10;bnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMu&#10;YWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+QWRvYmUgSW1h&#10;Z2VSZWFkeTwveG1wOkNyZWF0b3JUb29sPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj43Mjwv&#10;dGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmll&#10;bnRhdGlvbj4KICAgICAgICAgPHRpZmY6WFJlc29sdXRpb24+NzI8L3RpZmY6WFJlc29sdXRpb24+&#10;CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpg60/Z&#10;AAAXv0lEQVR4Ae1caZBdxXXuvve+bXaNFhAIJI0EWmZGCEaIJYVLBlxFlkpBbBGXKwRXYkOFwmAW&#10;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&#10;rkJggg==&#10;" id="image1" x="10" y="10" />
+</svg>
diff --git a/tests/manual/svg/data/image/data.png b/tests/manual/svg/data/image/data.png
new file mode 100644
index 0000000000..7b660be3de
--- /dev/null
+++ b/tests/manual/svg/data/image/data.png
Binary files differ
diff --git a/tests/manual/svg/data/image/qtlogo.png b/tests/manual/svg/data/image/qtlogo.png
new file mode 100644
index 0000000000..7b660be3de
--- /dev/null
+++ b/tests/manual/svg/data/image/qtlogo.png
Binary files differ
diff --git a/tests/manual/tableview/abstracttablemodel/main.qml b/tests/manual/tableview/abstracttablemodel/main.qml
index 5578e86dba..9c43af2f99 100644
--- a/tests/manual/tableview/abstracttablemodel/main.qml
+++ b/tests/manual/tableview/abstracttablemodel/main.qml
@@ -85,6 +85,22 @@ ApplicationWindow {
}
CheckBox {
+ id: movableColumnEnabled
+ checkable: true
+ checked: false
+ Layout.fillWidth: false
+ text: "Reorder columns"
+ }
+
+ CheckBox {
+ id: movableRowEnabled
+ checkable: true
+ checked: false
+ Layout.fillWidth: false
+ text: "Reorder rows"
+ }
+
+ CheckBox {
id: enableAnimation
checkable: true
checked: true
@@ -202,6 +218,14 @@ ApplicationWindow {
text: "Clear selection"
onClicked: tableView.selectionModel.clearSelection()
}
+ Button {
+ text: "Clear column reordering"
+ onClicked: tableView.clearColumnReordering()
+ }
+ Button {
+ text: "Clear row reordering"
+ onClicked: tableView.clearRowReordering()
+ }
}
}
@@ -450,7 +474,7 @@ ApplicationWindow {
}
}
- TableView {
+ HorizontalHeaderView {
id: topHeader
objectName: "topHeader"
anchors.left: centerScrollView.left
@@ -465,8 +489,11 @@ ApplicationWindow {
}
delegate: Rectangle {
+ required property bool containsDrag
implicitHeight: topHeader.height
implicitWidth: 20
+ border.width: containsDrag ? 1 : 0
+ border.color: containsDrag ? window.palette.text : window.palette.alternateBase
color: window.palette.alternateBase
Label {
anchors.centerIn: parent
@@ -481,10 +508,11 @@ ApplicationWindow {
syncView: tableView
syncDirection: Qt.Horizontal
+ movableColumns: movableColumnEnabled.checked
resizableColumns: resizableColumnsEnabled.checked
}
- TableView {
+ VerticalHeaderView {
id: leftHeader
objectName: "leftHeader"
anchors.left: menu.right
@@ -499,8 +527,11 @@ ApplicationWindow {
}
delegate: Rectangle {
+ required property bool containsDrag
implicitHeight: 50
implicitWidth: leftHeader.width
+ border.width: containsDrag ? 1 : 0
+ border.color: containsDrag ? window.palette.text : window.palette.alternateBase
color: window.palette.alternateBase
Label {
anchors.centerIn: parent
@@ -515,6 +546,7 @@ ApplicationWindow {
syncView: tableView
syncDirection: Qt.Vertical
+ movableRows: movableRowEnabled.checked
resizableRows: resizableRowsEnabled.checked
}