diff options
Diffstat (limited to 'tests/manual')
37 files changed, 1520 insertions, 46 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..2c066381a9 --- /dev/null +++ b/tests/manual/quickcontrols/menus/Main.qml @@ -0,0 +1,472 @@ +// 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.popupType() + 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") } + Menu { + title: qsTr("SubSub...") + ContextAction { text: qsTr("SubSub action 1") } + ContextAction { text: qsTr("SubSub 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.popupType() + 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.popupType() + 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: ["Item", "Window", "Native"] + onCurrentIndexChanged: CppSettings.popupType = currentIndex + currentIndex: CppSettings.popupType + + function popupType() { + if (currentText === "Window") + return Popup.Window + else if (currentText === "Native") + return Popup.Native + else + return Popup.Item + } + } + } + + 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.popupType() + + 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: popupTypeCombo.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 { + title: qsTr("SubSub...") + ContextAction { text: qsTr("SubSub action 1") } + ContextAction { text: qsTr("SubSub action 2") } + } + } + } + + 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 Binary files differnew file mode 100644 index 0000000000..590a61eb80 --- /dev/null +++ b/tests/manual/quickcontrols/menus/icons/warning.png diff --git a/tests/manual/quickcontrols/menus/icons/warning@2x.png b/tests/manual/quickcontrols/menus/icons/warning@2x.png Binary files differnew file mode 100644 index 0000000000..487fbafcfd --- /dev/null +++ b/tests/manual/quickcontrols/menus/icons/warning@2x.png 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/quickcontrols/testbench/controls/Menu.qml b/tests/manual/quickcontrols/testbench/controls/Menu.qml index df5bc55d20..f1948a7399 100644 --- a/tests/manual/quickcontrols/testbench/controls/Menu.qml +++ b/tests/manual/quickcontrols/testbench/controls/Menu.qml @@ -42,15 +42,18 @@ QtObject { MenuItem { text: "Checked" + checkable: true checked: true } MenuItem { text: "Checked + Pressed" + checkable: true checked: true down: true } MenuItem { text: "Checked + Disabled" + checkable: true checked: true enabled: false } diff --git a/tests/manual/quickcontrols/testbench/controls/ToolBar.qml b/tests/manual/quickcontrols/testbench/controls/ToolBar.qml index 2596f4309e..867556d152 100644 --- a/tests/manual/quickcontrols/testbench/controls/ToolBar.qml +++ b/tests/manual/quickcontrols/testbench/controls/ToolBar.qml @@ -34,6 +34,8 @@ QtObject { ToolButton { text: qsTr("ToolButton 3") + checkable: true + checked: true } } } diff --git a/tests/manual/quickcontrols/testbench/testbench.qml b/tests/manual/quickcontrols/testbench/testbench.qml index 9c7f4a70aa..68975cfd69 100644 --- a/tests/manual/quickcontrols/testbench/testbench.qml +++ b/tests/manual/quickcontrols/testbench/testbench.qml @@ -139,7 +139,11 @@ Ui.ApplicationWindow { text: "\ue801" font.family: "fontello" visible: searchTextField.length > 0 - onClicked: searchTextField.clear() + onClicked: { + searchTextField.clear() + // textEdited is not emitted for clear(), so we have to set this ourselves. + settings.lastSearchText = "" + } Layout.leftMargin: -5 } diff --git a/tests/manual/svg/data/styling/stroking_text.svg b/tests/manual/svg/data/styling/stroking_text.svg new file mode 100644 index 0000000000..bacc04fff4 --- /dev/null +++ b/tests/manual/svg/data/styling/stroking_text.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="250" height="200"> + <text y="70" style="font-size: 80px; font-weight: bold; font-family: sans-serif; stroke-dasharray:5,5; fill: peachpuff; stroke: crimson; stroke-width:4.1px; stroke-linecap: square; stroke-linejoin: bevel;">pizazz</text> + <text y="150" style="font-size: 80px; font-weight: bold; font-family: sans-serif; fill: peachpuff; stroke: blue; stroke-width:4.1px; stroke-linecap: square; stroke-linejoin: bevel;">pizazz</text> +</svg> 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 } diff --git a/tests/manual/windowembedding/examples/clipping.qml b/tests/manual/windowembedding/examples/clipping.qml index fa3d27db72..5e7778da37 100644 --- a/tests/manual/windowembedding/examples/clipping.qml +++ b/tests/manual/windowembedding/examples/clipping.qml @@ -16,17 +16,16 @@ Rectangle { clip: true - Window { - id: redWindow - flags: Qt.WindowTransparentForInput - color: "lightgray" - visible: true + WindowContainer { width: 200; height: 200 - parent: flickable.contentItem + window: Window { + flags: Qt.WindowTransparentForInput + color: "lightgray" - Image { - source: "https://placedog.net/500/500?random" - anchors.fill: parent + Image { + source: "https://placedog.net/500/500?random" + anchors.fill: parent + } } } } diff --git a/tests/manual/windowembedding/examples/stacking.qml b/tests/manual/windowembedding/examples/stacking.qml index 7634bb0bff..38a84fa477 100644 --- a/tests/manual/windowembedding/examples/stacking.qml +++ b/tests/manual/windowembedding/examples/stacking.qml @@ -10,41 +10,41 @@ Rectangle { property int windowZ: 0 - Window { + WindowContainer { id: redWindow - color: "red" - visible: true - parent: rootItem + window: Window { + color: "red" - MouseArea { - anchors.fill: parent - onClicked: redWindow.z = ++rootItem.windowZ + MouseArea { + anchors.fill: parent + onClicked: redWindow.z = ++rootItem.windowZ + } } } - Window { + WindowContainer { id: greenWindow - color: "green" - visible: true - parent: rootItem x: 100; y: 100 + window: Window { + color: "green" - MouseArea { - anchors.fill: parent - onClicked: greenWindow.z = ++rootItem.windowZ + MouseArea { + anchors.fill: parent + onClicked: greenWindow.z = ++rootItem.windowZ + } } } - Window { + WindowContainer { id: blueWindow - color: "blue" - visible: true - parent: rootItem x: 200; y: 200 + window: Window { + color: "blue" - MouseArea { - anchors.fill: parent - onClicked: blueWindow.z = ++rootItem.windowZ + MouseArea { + anchors.fill: parent + onClicked: blueWindow.z = ++rootItem.windowZ + } } } } diff --git a/tests/manual/windowembedding/examples/transform.qml b/tests/manual/windowembedding/examples/transform.qml index c07dae5850..932c52b0ab 100644 --- a/tests/manual/windowembedding/examples/transform.qml +++ b/tests/manual/windowembedding/examples/transform.qml @@ -19,22 +19,21 @@ Rectangle { ] //scale: 3 // FIXME: Doesn't work when assigned like this - Window { - id: childWindow - objectName: "childWindow" - visible: true - parent: rectangle + WindowContainer { width: 200; height: 200 + window: Window { + color: "lightgray" - Image { - source: "https://placedog.net/500/500?random" - anchors.fill: parent - } + Image { + source: "https://placedog.net/500/500?random" + anchors.fill: parent + } - MouseArea { - anchors.fill: parent - onClicked: { - rectangle.scale += 0.1 + MouseArea { + anchors.fill: parent + onClicked: { + rectangle.scale += 0.1 + } } } } |