summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKimmo Leppälä <kimmo.leppala@qt.io>2023-04-14 22:21:43 +0300
committerKai Köhne <kai.koehne@qt.io>2023-05-09 18:37:33 +0200
commit02efcba73d7f7d1d1eb49a6741558b2b6528838b (patch)
tree6cc527060bb4385220ffced8358c689702a8b16f
parent8cf277e3f21d8170964363888ebb6f01c730f013 (diff)
downloadqtdoc-02efcba73d7f7d1d1eb49a6741558b2b6528838b.tar.gz
Move robotarm example from quick3d to qtdoc
Pick-to: 6.5 Change-Id: Iac113bc7ed773f291ef09ebe144e1de1e0e58136 Reviewed-by: Kai Köhne <kai.koehne@qt.io>
-rw-r--r--examples/demos/CMakeLists.txt3
-rw-r--r--examples/demos/robotarm/Backend/CMakeLists.txt14
-rw-r--r--examples/demos/robotarm/Backend/animatedparam.cpp35
-rw-r--r--examples/demos/robotarm/Backend/animatedparam.h32
-rw-r--r--examples/demos/robotarm/Backend/backend.cpp115
-rw-r--r--examples/demos/robotarm/Backend/backend.h66
-rw-r--r--examples/demos/robotarm/CMakeLists.txt32
-rw-r--r--examples/demos/robotarm/RobotArm.qmlproject92
-rw-r--r--examples/demos/robotarm/backend_mock/Backend/BackendMock.qml41
-rw-r--r--examples/demos/robotarm/backend_mock/Backend/qmldir2
-rw-r--r--examples/demos/robotarm/content/App.qml20
-rw-r--r--examples/demos/robotarm/content/CMakeLists.txt31
-rw-r--r--examples/demos/robotarm/content/LabeledSlider.ui.qml29
-rw-r--r--examples/demos/robotarm/content/MainScreen.ui.qml389
-rw-r--r--examples/demos/robotarm/content/NodeIndicator.qml58
-rw-r--r--examples/demos/robotarm/content/RoboticArm.ui.qml163
-rw-r--r--examples/demos/robotarm/content/Toggle.ui.qml30
-rw-r--r--examples/demos/robotarm/content/fonts/fonts.txt1
-rw-r--r--examples/demos/robotarm/content/maps/qt.pngbin0 -> 15997 bytes
-rw-r--r--examples/demos/robotarm/content/meshes/arm.meshbin0 -> 199480 bytes
-rw-r--r--examples/demos/robotarm/content/meshes/base.meshbin0 -> 385752 bytes
-rw-r--r--examples/demos/robotarm/content/meshes/forearm.meshbin0 -> 611992 bytes
-rw-r--r--examples/demos/robotarm/content/meshes/hand.meshbin0 -> 123928 bytes
-rw-r--r--examples/demos/robotarm/content/meshes/hand_grab_b.meshbin0 -> 63212 bytes
-rw-r--r--examples/demos/robotarm/content/meshes/hand_grab_b_hinge_1.meshbin0 -> 26324 bytes
-rw-r--r--examples/demos/robotarm/content/meshes/hand_grab_b_hinge_2.meshbin0 -> 26324 bytes
-rw-r--r--examples/demos/robotarm/content/meshes/hand_grab_t.meshbin0 -> 63212 bytes
-rw-r--r--examples/demos/robotarm/content/meshes/hand_grab_t_hinge_1.meshbin0 -> 26324 bytes
-rw-r--r--examples/demos/robotarm/content/meshes/hand_grab_t_hinge_2.meshbin0 -> 26324 bytes
-rw-r--r--examples/demos/robotarm/content/meshes/hand_hinge.meshbin0 -> 6440 bytes
-rw-r--r--examples/demos/robotarm/content/meshes/root.meshbin0 -> 188984 bytes
-rw-r--r--examples/demos/robotarm/doc/images/robotarm-example.pngbin0 -> 52621 bytes
-rw-r--r--examples/demos/robotarm/doc/src/robotarm.qdoc23
-rw-r--r--examples/demos/robotarm/imports/CMakeLists.txt4
-rw-r--r--examples/demos/robotarm/imports/RobotArm/CMakeLists.txt16
-rw-r--r--examples/demos/robotarm/imports/RobotArm/Constants.qml22
-rw-r--r--examples/demos/robotarm/imports/RobotArm/designer/plugin.metainfo13
-rw-r--r--examples/demos/robotarm/imports/RobotArm/qmldir5
-rw-r--r--examples/demos/robotarm/main.qml11
-rw-r--r--examples/demos/robotarm/qmlmodules23
-rw-r--r--examples/demos/robotarm/qtquickcontrols2.conf6
-rw-r--r--examples/demos/robotarm/src/app_environment.h17
-rw-r--r--examples/demos/robotarm/src/import_qml_plugins.h12
-rw-r--r--examples/demos/robotarm/src/main.cpp43
-rw-r--r--tests/auto/quick/examples/tst_examples.cpp1
45 files changed, 1349 insertions, 0 deletions
diff --git a/examples/demos/CMakeLists.txt b/examples/demos/CMakeLists.txt
index 983ab909..de4bc228 100644
--- a/examples/demos/CMakeLists.txt
+++ b/examples/demos/CMakeLists.txt
@@ -26,3 +26,6 @@ endif()
if(TARGET Qt6::Widgets)
qt_internal_add_example(documentviewer)
endif()
+if(TARGET Qt::Quick AND TARGET Qt::QuickControls2 AND TARGET Qt::Quick3D)
+ qt_internal_add_example(robotarm)
+endif()
diff --git a/examples/demos/robotarm/Backend/CMakeLists.txt b/examples/demos/robotarm/Backend/CMakeLists.txt
new file mode 100644
index 00000000..4d2e1eb4
--- /dev/null
+++ b/examples/demos/robotarm/Backend/CMakeLists.txt
@@ -0,0 +1,14 @@
+find_package(Qt6 REQUIRED COMPONENTS Gui)
+
+qt_add_qml_module(backendmodule
+ URI Backend
+ VERSION 1.0
+ SOURCES
+ animatedparam.cpp
+ animatedparam.h
+ backend.cpp
+ backend.h
+ RESOURCE_PREFIX "/"
+)
+
+target_link_libraries(backendmodule PUBLIC Qt6::Gui)
diff --git a/examples/demos/robotarm/Backend/animatedparam.cpp b/examples/demos/robotarm/Backend/animatedparam.cpp
new file mode 100644
index 00000000..e72678e3
--- /dev/null
+++ b/examples/demos/robotarm/Backend/animatedparam.cpp
@@ -0,0 +1,35 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "animatedparam.h"
+
+#include <QVariantAnimation>
+
+AnimatedParam::AnimatedParam(QObject *parent) : QVariantAnimation(parent)
+{
+ setDuration(1500);
+ setEasingCurve(QEasingCurve::InOutCubic);
+
+ connect(this, &QVariantAnimation::valueChanged, this, &AnimatedParam::valueChanged);
+ connect(this, &QAbstractAnimation::stateChanged, this, [this](QAbstractAnimation::State newState, QAbstractAnimation::State) {
+ m_isRunning = (newState == QAbstractAnimation::Running);
+ });
+}
+
+int AnimatedParam::value() const
+{
+ return currentValue().toInt();
+}
+
+void AnimatedParam::setValue(int newValue)
+{
+ stop();
+ setStartValue(value());
+ setEndValue(newValue);
+ start();
+}
+
+bool AnimatedParam::isRunning() const
+{
+ return m_isRunning;
+}
diff --git a/examples/demos/robotarm/Backend/animatedparam.h b/examples/demos/robotarm/Backend/animatedparam.h
new file mode 100644
index 00000000..e88576f7
--- /dev/null
+++ b/examples/demos/robotarm/Backend/animatedparam.h
@@ -0,0 +1,32 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef ANIMATEDPARAM_H
+#define ANIMATEDPARAM_H
+
+#include <QProperty>
+#include <QVariantAnimation>
+
+//! [class definition]
+class AnimatedParam : public QVariantAnimation
+{
+ Q_OBJECT
+ Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
+ //! [class definition]
+
+public:
+ AnimatedParam(QObject *parent = nullptr);
+
+ int value() const;
+ void setValue(int newValue);
+
+ bool isRunning() const;
+
+signals:
+ void valueChanged();
+
+private:
+ QProperty<bool> m_isRunning;
+};
+
+#endif // ANIMATEDPARAM_H
diff --git a/examples/demos/robotarm/Backend/backend.cpp b/examples/demos/robotarm/Backend/backend.cpp
new file mode 100644
index 00000000..e51aa13e
--- /dev/null
+++ b/examples/demos/robotarm/Backend/backend.cpp
@@ -0,0 +1,115 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "backend.h"
+#include <QTransform>
+
+Backend::Backend(QObject *parent) : QObject(parent)
+{
+ connect(&m_rotation1Angle, &AnimatedParam::valueChanged, this, &Backend::rot1AngleChanged);
+ connect(&m_rotation2Angle, &AnimatedParam::valueChanged, this, &Backend::rot2AngleChanged);
+ connect(&m_rotation3Angle, &AnimatedParam::valueChanged, this, &Backend::rot3AngleChanged);
+ connect(&m_rotation4Angle, &AnimatedParam::valueChanged, this, &Backend::rot4AngleChanged);
+ connect(&m_clawsAngle, &AnimatedParam::valueChanged, this, &Backend::clawsAngleChanged);
+
+ m_status.setBinding([this]() {
+ return m_isCollision.value() ? QString("Collision!")
+ : m_rotation1Angle.isRunning() || m_rotation2Angle.isRunning() || m_rotation3Angle.isRunning()
+ || m_rotation4Angle.isRunning()
+ ? QString("Busy")
+ : QString("Ready");
+ });
+
+ connect(&m_rotation1Angle, &AnimatedParam::valueChanged, this, &Backend::detectCollision);
+ connect(&m_rotation2Angle, &AnimatedParam::valueChanged, this, &Backend::detectCollision);
+ connect(&m_rotation3Angle, &AnimatedParam::valueChanged, this, &Backend::detectCollision);
+ connect(&m_rotation4Angle, &AnimatedParam::valueChanged, this, &Backend::detectCollision);
+}
+
+int Backend::rotation1Angle() const
+{
+ return m_rotation1Angle.value();
+}
+
+void Backend::setRot1Angle(const int angle)
+{
+ m_rotation1Angle.setValue(angle);
+}
+
+int Backend::rotation2Angle() const
+{
+ return m_rotation2Angle.value();
+}
+
+void Backend::setRot2Angle(const int angle)
+{
+ m_rotation2Angle.setValue(angle);
+}
+
+int Backend::rotation3Angle() const
+{
+ return m_rotation3Angle.value();
+}
+
+void Backend::setRot3Angle(const int angle)
+{
+ m_rotation3Angle.setValue(angle);
+}
+
+int Backend::rotation4Angle() const
+{
+ return m_rotation4Angle.value();
+}
+
+void Backend::setRot4Angle(const int angle)
+{
+ m_rotation4Angle.setValue(angle);
+}
+
+int Backend::clawsAngle() const
+{
+ return m_clawsAngle.value();
+}
+
+void Backend::setClawsAngle(const int angle)
+{
+ m_clawsAngle.setValue(angle);
+}
+
+QString Backend::status() const
+{
+ return m_status;
+}
+
+QBindable<QString> Backend::bindableStatus() const
+{
+ return &m_status;
+}
+
+void Backend::detectCollision()
+{
+ // simple aproximate collision detection, uses hardcoded model dimensions
+
+ QPolygon pol1(QRect(-70, 0, 70, 300));
+
+ QTransform t;
+
+ t.rotate(8.7);
+ t.translate(0, 259);
+
+ t.rotate(-20.);
+ t.rotate(rotation3Angle());
+
+ QPolygon pol2 = t.mapToPolygon(QRect(-35, 0, 35, 233));
+ t.translate(0, 233);
+ t.rotate(15);
+ t.rotate(rotation2Angle());
+
+ QPolygon pol3 = t.mapToPolygon(QRect(-27, 0, 27, 212));
+ t.translate(0, 212);
+ t.rotate(rotation1Angle());
+
+ QPolygon pol4 = t.mapToPolygon(QRect(-42, 0, 42, 180));
+
+ m_isCollision.setValue(pol1.intersects(pol3) || pol1.intersects(pol4) || pol2.intersects(pol4));
+}
diff --git a/examples/demos/robotarm/Backend/backend.h b/examples/demos/robotarm/Backend/backend.h
new file mode 100644
index 00000000..e7b92234
--- /dev/null
+++ b/examples/demos/robotarm/Backend/backend.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef BACKEND_H
+#define BACKEND_H
+
+#include "animatedparam.h"
+
+#include <QObject>
+#include <qqmlregistration.h>
+
+//! [class definition]
+class Backend : public QObject
+{
+ Q_OBJECT
+ QML_ELEMENT
+ Q_PROPERTY(int rotation1Angle READ rotation1Angle WRITE setRot1Angle NOTIFY rot1AngleChanged)
+ Q_PROPERTY(int rotation2Angle READ rotation2Angle WRITE setRot2Angle NOTIFY rot2AngleChanged)
+ Q_PROPERTY(int rotation3Angle READ rotation3Angle WRITE setRot3Angle NOTIFY rot3AngleChanged)
+ Q_PROPERTY(int rotation4Angle READ rotation4Angle WRITE setRot4Angle NOTIFY rot4AngleChanged)
+ Q_PROPERTY(int clawsAngle READ clawsAngle WRITE setClawsAngle NOTIFY clawsAngleChanged)
+ Q_PROPERTY(QString status READ status BINDABLE bindableStatus)
+ //! [class definition]
+
+public:
+ explicit Backend(QObject *parent = nullptr);
+
+ int rotation1Angle() const;
+ void setRot1Angle(const int angle);
+
+ int rotation2Angle() const;
+ void setRot2Angle(const int angle);
+
+ int rotation3Angle() const;
+ void setRot3Angle(const int angle);
+
+ int rotation4Angle() const;
+ void setRot4Angle(const int angle);
+
+ int clawsAngle() const;
+ void setClawsAngle(const int angle);
+
+ QString status() const;
+ QBindable<QString> bindableStatus() const;
+
+signals:
+ void rot1AngleChanged();
+ void rot2AngleChanged();
+ void rot3AngleChanged();
+ void rot4AngleChanged();
+ void clawsAngleChanged();
+
+private:
+ AnimatedParam m_rotation1Angle;
+ AnimatedParam m_rotation2Angle;
+ AnimatedParam m_rotation3Angle;
+ AnimatedParam m_rotation4Angle;
+ AnimatedParam m_clawsAngle;
+
+ QProperty<QString> m_status;
+ QProperty<bool> m_isCollision;
+
+ void detectCollision();
+};
+
+#endif // BACKEND_H
diff --git a/examples/demos/robotarm/CMakeLists.txt b/examples/demos/robotarm/CMakeLists.txt
new file mode 100644
index 00000000..d718b18c
--- /dev/null
+++ b/examples/demos/robotarm/CMakeLists.txt
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.16)
+
+project(RobotArm LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick3d/robotarm")
+
+find_package(Qt6 COMPONENTS Gui Qml Quick)
+qt_standard_project_setup()
+qt_add_executable(RobotArmApp src/main.cpp)
+
+qt_add_resources(RobotArmApp "configuration"
+ PREFIX "/"
+ FILES
+ qtquickcontrols2.conf
+)
+
+target_link_libraries(RobotArmApp PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::Quick
+)
+
+add_subdirectory(Backend)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules)
diff --git a/examples/demos/robotarm/RobotArm.qmlproject b/examples/demos/robotarm/RobotArm.qmlproject
new file mode 100644
index 00000000..69a8958f
--- /dev/null
+++ b/examples/demos/robotarm/RobotArm.qmlproject
@@ -0,0 +1,92 @@
+import QmlProject
+
+Project {
+ mainFile: "content/App.qml"
+
+ /* Include .qml, .js, and image files from current directory and subdirectories */
+ QmlFiles {
+ directory: "content"
+ }
+
+ QmlFiles {
+ directory: "imports"
+ }
+
+ QmlFiles {
+ directory: "backend_mock"
+ }
+
+ JavaScriptFiles {
+ directory: "content"
+ }
+
+ JavaScriptFiles {
+ directory: "imports"
+ }
+
+ ImageFiles {
+ directory: "content"
+ }
+
+ Files {
+ filter: "*.conf"
+ files: ["qtquickcontrols2.conf"]
+ }
+
+ Files {
+ filter: "qmldir"
+ directory: "."
+ }
+
+ Files {
+ filter: "*.ttf;*.otf"
+ }
+
+ Files {
+ filter: "*.wav;*.mp3"
+ }
+
+ Files {
+ filter: "*.mp4"
+ }
+
+ Files {
+ filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag"
+ }
+
+ Files {
+ filter: "*.mesh"
+ directory: "asset_imports"
+ }
+
+ Environment {
+ QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf"
+ QT_AUTO_SCREEN_SCALE_FACTOR: "1"
+ QT_LOGGING_RULES: "qt.qml.connections=false"
+ QT_ENABLE_HIGHDPI_SCALING: "0"
+ /* Useful for debugging
+ QSG_VISUALIZE=batches
+ QSG_VISUALIZE=clip
+ QSG_VISUALIZE=changes
+ QSG_VISUALIZE=overdraw
+ */
+ }
+
+ qt6Project: true
+
+ /* List of plugin directories passed to QML runtime */
+ importPaths: [ "imports", "asset_imports", "backend_mock" ]
+
+ /* Required for deployment */
+ targetDirectory: "/opt/RobotArm"
+
+ qdsVersion: "3.0"
+
+ /* If any modules the project imports require widgets (e.g. QtCharts), widgetApp must be true */
+ widgetApp: true
+
+ multilanguageSupport: true
+ supportedLanguages: ["en"]
+ primaryLanguage: "en"
+
+}
diff --git a/examples/demos/robotarm/backend_mock/Backend/BackendMock.qml b/examples/demos/robotarm/backend_mock/Backend/BackendMock.qml
new file mode 100644
index 00000000..3ee8c7f2
--- /dev/null
+++ b/examples/demos/robotarm/backend_mock/Backend/BackendMock.qml
@@ -0,0 +1,41 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+QtObject {
+ property int rotation1Angle
+ property int rotation2Angle
+ property int rotation3Angle
+ property int rotation4Angle
+ property int clawsAngle
+ readonly property string status: "Ready"
+
+ Behavior on rotation1Angle {
+ SmoothedAnimation {
+ velocity: 100
+ }
+ }
+
+ Behavior on rotation2Angle {
+ SmoothedAnimation {
+ velocity: 80
+ }
+ }
+
+ Behavior on rotation3Angle {
+ SmoothedAnimation {
+ velocity: 60
+ }
+ }
+
+ Behavior on rotation4Angle {
+ SmoothedAnimation {
+ velocity: 60
+ }
+ }
+
+ Behavior on clawsAngle {
+ SmoothedAnimation {}
+ }
+}
diff --git a/examples/demos/robotarm/backend_mock/Backend/qmldir b/examples/demos/robotarm/backend_mock/Backend/qmldir
new file mode 100644
index 00000000..c5f8357e
--- /dev/null
+++ b/examples/demos/robotarm/backend_mock/Backend/qmldir
@@ -0,0 +1,2 @@
+module Backend
+Backend 1.0 BackendMock.qml
diff --git a/examples/demos/robotarm/content/App.qml b/examples/demos/robotarm/content/App.qml
new file mode 100644
index 00000000..1cd1fdf9
--- /dev/null
+++ b/examples/demos/robotarm/content/App.qml
@@ -0,0 +1,20 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import RobotArm
+
+Window {
+ width: Constants.width
+ height: Constants.height
+
+ minimumWidth: 800
+ minimumHeight: 600
+
+ visible: true
+ title: "RobotArm"
+
+ MainScreen {
+ anchors.fill: parent
+ }
+}
diff --git a/examples/demos/robotarm/content/CMakeLists.txt b/examples/demos/robotarm/content/CMakeLists.txt
new file mode 100644
index 00000000..5a6d289f
--- /dev/null
+++ b/examples/demos/robotarm/content/CMakeLists.txt
@@ -0,0 +1,31 @@
+### This file is automatically generated by Qt Design Studio.
+### Do not change
+
+qt_add_library(content STATIC)
+qt6_add_qml_module(content
+ URI "content"
+ VERSION 1.0
+ QML_FILES
+ App.qml
+ LabeledSlider.ui.qml
+ MainScreen.ui.qml
+ NodeIndicator.qml
+ RoboticArm.ui.qml
+ Toggle.ui.qml
+ RESOURCES
+ fonts/fonts.txt
+ maps/qt.png
+ meshes/arm.mesh
+ meshes/base.mesh
+ meshes/forearm.mesh
+ meshes/hand.mesh
+ meshes/hand_grab_b.mesh
+ meshes/hand_grab_b_hinge_1.mesh
+ meshes/hand_grab_b_hinge_2.mesh
+ meshes/hand_grab_t.mesh
+ meshes/hand_grab_t_hinge_1.mesh
+ meshes/hand_grab_t_hinge_2.mesh
+ meshes/hand_hinge.mesh
+ meshes/root.mesh
+ RESOURCE_PREFIX "/"
+)
diff --git a/examples/demos/robotarm/content/LabeledSlider.ui.qml b/examples/demos/robotarm/content/LabeledSlider.ui.qml
new file mode 100644
index 00000000..58a328bc
--- /dev/null
+++ b/examples/demos/robotarm/content/LabeledSlider.ui.qml
@@ -0,0 +1,29 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+/*
+This is a UI file (.ui.qml) that is intended to be edited in Qt Design Studio only.
+It is supposed to be strictly declarative and only uses a subset of QML. If you edit
+this file manually, you might introduce QML code that is not supported by Qt Design Studio.
+Check out https://doc.qt.io/qtcreator/creator-quick-ui-forms.html for details on .ui.qml files.
+*/
+import QtQuick
+import QtQuick.Controls
+
+Slider {
+ property string labelText: qsTr("Text")
+ stepSize: 1
+
+ Label {
+ text: parent.labelText
+ anchors.left: parent.left
+ anchors.bottom: parent.top
+ bottomPadding: -12
+ }
+ Label {
+ text: parent.value
+ anchors.right: parent.right
+ anchors.bottom: parent.top
+ bottomPadding: -12
+ }
+}
diff --git a/examples/demos/robotarm/content/MainScreen.ui.qml b/examples/demos/robotarm/content/MainScreen.ui.qml
new file mode 100644
index 00000000..1af531e3
--- /dev/null
+++ b/examples/demos/robotarm/content/MainScreen.ui.qml
@@ -0,0 +1,389 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick3D
+import QtQuick.Controls.Material
+import QtQuick.Controls
+import QtQuick.Layouts
+import RobotArm
+import Backend
+import QtQml
+
+Pane {
+ id: root
+ Material.theme: darkModeToggle.checked ? Material.Dark : Material.Light
+
+ readonly property bool mobile: Qt.platform.os === "android"
+ readonly property bool horizontal: width > height
+ property real sliderWidth: width * 0.15
+ property real buttonRowWidth: width * 0.12
+ property real buttonMinWidth: 65
+
+ leftPadding: 60
+ rightPadding: 60
+ topPadding: 50
+ bottomPadding: 50
+
+ width: 800
+ height: 600
+ state: "mobileHorizontal"
+
+ Backend {
+ id: backend
+ rotation1Angle: rotation1Slider.value
+ rotation2Angle: rotation2Slider.value
+ rotation3Angle: rotation3Slider.value
+ rotation4Angle: rotation4Slider.value
+ clawsAngle: clawToggle.checked ? 0 : 90
+ }
+
+ Toggle {
+ id: darkModeToggle
+ text: qsTr("Dark mode")
+ anchors.top: parent.top
+ }
+
+ ColumnLayout {
+ id: slidersColumn
+ spacing: 6
+ anchors.bottom: parent.bottom
+
+ LabeledSlider {
+ id: rotation1Slider
+ Layout.preferredWidth: root.sliderWidth
+ Layout.minimumWidth: 160
+ labelText: "Rotation 1"
+ from: -90
+ to: 90
+ value: 60
+ }
+
+ LabeledSlider {
+ id: rotation2Slider
+ Layout.preferredWidth: root.sliderWidth
+ Layout.minimumWidth: 160
+ labelText: "Rotation 2"
+ from: -135
+ to: 135
+ value: 45
+ }
+
+ LabeledSlider {
+ id: rotation3Slider
+ Layout.preferredWidth: root.sliderWidth
+ Layout.minimumWidth: 160
+ labelText: "Rotation 3"
+ from: -90
+ to: 90
+ value: 45
+ }
+
+ LabeledSlider {
+ id: rotation4Slider
+ Layout.preferredWidth: root.sliderWidth
+ Layout.minimumWidth: 160
+ labelText: "Rotation 4"
+ from: -180
+ to: 180
+ }
+ }
+
+ Toggle {
+ id: clawToggle
+ text: qsTr("Claw")
+ anchors.bottom: slidersColumn.top
+ anchors.bottomMargin: 30
+ }
+
+ GridLayout {
+ id: buttonsRow
+ columns: 2
+ rows: 2
+ columnSpacing: 16
+ rowSpacing: 8
+ anchors.bottom: clawToggle.top
+ anchors.bottomMargin: 30
+
+ Button {
+ id: pose1
+ text: qsTr("Pose 1")
+ Layout.preferredWidth: root.buttonRowWidth / 2
+ Layout.minimumWidth: root.buttonMinWidth
+ Layout.preferredHeight: 45
+
+ Connections {
+ target: pose1
+ onClicked: {
+ rotation1Slider.value = 30
+ rotation2Slider.value = 60
+ rotation3Slider.value = 90
+ rotation4Slider.value = 145
+ }
+ }
+ }
+
+ Button {
+ id: pose2
+ text: qsTr("Pose 2")
+ Layout.preferredWidth: root.buttonRowWidth / 2
+ Layout.minimumWidth: root.buttonMinWidth
+ Layout.preferredHeight: 45
+
+ Connections {
+ target: pose2
+ onClicked: {
+ rotation1Slider.value = 60
+ rotation2Slider.value = 45
+ rotation3Slider.value = 45
+ rotation4Slider.value = 60
+ }
+ }
+ }
+
+ Button {
+ id: pose3
+ text: qsTr("Pose 3")
+ Layout.preferredWidth: root.buttonRowWidth / 2
+ Layout.minimumWidth: root.buttonMinWidth
+ Layout.preferredHeight: 45
+
+ Connections {
+ target: pose3
+ onClicked: {
+ rotation1Slider.value = -90
+ rotation2Slider.value = -60
+ rotation3Slider.value = -45
+ rotation4Slider.value = -180
+ }
+ }
+ }
+
+ Button {
+ id: resetPose
+ text: qsTr("Reset")
+ Layout.preferredWidth: root.buttonRowWidth / 2
+ Layout.minimumWidth: root.buttonMinWidth
+ Layout.preferredHeight: 45
+
+ Connections {
+ target: resetPose
+ onClicked: {
+ rotation1Slider.value = 0
+ rotation2Slider.value = 0
+ rotation3Slider.value = 0
+ rotation4Slider.value = 0
+ clawToggle.checked = false
+ }
+ }
+ }
+ }
+
+ View3D {
+ anchors.fill: parent
+
+ camera: camera
+ Node {
+ id: scene
+
+ PointLight {
+ x: 760
+ z: 770
+ quadraticFade: 0
+ brightness: 1
+ }
+
+ DirectionalLight {
+ eulerRotation.z: 30
+ eulerRotation.y: -165
+ }
+
+ DirectionalLight {
+ y: 1000
+ brightness: 0.4
+ eulerRotation.z: -180
+ eulerRotation.y: 90
+ eulerRotation.x: -90
+ }
+
+ PerspectiveCamera {
+ id: camera
+ x: 1050
+ y: 375
+ z: -40
+ pivot.x: 200
+ eulerRotation.y: 85
+ }
+ RoboticArm {
+ id: roboArm
+ rotation1: backend.rotation1Angle
+ rotation2: backend.rotation2Angle
+ rotation3: backend.rotation3Angle
+ rotation4: backend.rotation4Angle
+ clawsAngle: backend.clawsAngle
+ }
+ }
+
+ NodeIndicator {
+ scenePosition: roboArm.hand_position
+ isFocused: clawToggle.hasFocus
+ label: clawToggle.text
+ size: 30
+ }
+
+ NodeIndicator {
+ scenePosition: roboArm.hand_hinge_position
+ isFocused: rotation1Slider.activeFocus
+ label: rotation1Slider.labelText
+ size: 40
+ }
+
+ NodeIndicator {
+ scenePosition: roboArm.arm_position
+ isFocused: rotation2Slider.activeFocus
+ label: rotation2Slider.labelText
+ size: 50
+ }
+
+ NodeIndicator {
+ scenePosition: roboArm.forearm_position
+ isFocused: rotation3Slider.activeFocus
+ label: rotation3Slider.labelText
+ size: 60
+ }
+
+ NodeIndicator {
+ scenePosition: roboArm.root_position
+ isFocused: rotation4Slider.activeFocus
+ label: rotation4Slider.labelText
+ size: 60
+ }
+
+ environment: sceneEnvironment
+
+ SceneEnvironment {
+ id: sceneEnvironment
+ antialiasingQuality: SceneEnvironment.VeryHigh
+ antialiasingMode: SceneEnvironment.MSAA
+ }
+ }
+
+ Label {
+ id: robotStatus
+ text: backend.status
+ anchors.top: parent.top
+ font.italic: true
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.topMargin: 15
+ }
+
+ states: [
+ State {
+ name: "mobileHorizontal"
+ when: root.mobile && root.horizontal
+
+ PropertyChanges {
+ target: root
+ leftPadding: 45
+ topPadding: 15
+ bottomPadding: 0
+ sliderWidth: width * 0.4
+ buttonRowWidth: width * 0.2
+ buttonMinWidth: 75
+ }
+
+ PropertyChanges {
+ target: roboArm
+ z: -200
+ }
+ },
+ State {
+ name: "desktopVertical"
+ when: !root.mobile && !root.horizontal
+
+ PropertyChanges {
+ target: root
+ sliderWidth: width * 0.4
+ buttonRowWidth: width * 0.2
+ bottomPadding: 20
+ }
+ AnchorChanges {
+ target: slidersColumn
+ anchors.right: parent.right
+ }
+ PropertyChanges {
+ target: slidersColumn
+ anchors.rightMargin: 20
+ }
+
+ AnchorChanges {
+ target: buttonsRow
+ anchors.bottom: undefined
+ anchors.top: slidersColumn.top
+ }
+
+ AnchorChanges {
+ target: clawToggle
+ anchors.bottom: undefined
+ anchors.top: buttonsRow.bottom
+ }
+
+ PropertyChanges {
+ target: roboArm
+ scale.x: 0.7
+ scale.y: 0.7
+ scale.z: 0.7
+ y: 250
+ z: 150
+ }
+ },
+ State {
+ name: "mobileVertical"
+ when: root.mobile && !root.horizontal
+
+ PropertyChanges {
+ target: root
+ sliderWidth: width * 0.85
+ topPadding: 15
+ leftPadding: 45
+ bottomPadding: 0
+ buttonRowWidth: width * 0.2
+ buttonMinWidth: 75
+ }
+
+ AnchorChanges {
+ target: slidersColumn
+ anchors.left: undefined
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
+
+ AnchorChanges {
+ target: clawToggle
+ anchors.left: undefined
+ anchors.right: slidersColumn.right
+ }
+
+ AnchorChanges {
+ target: buttonsRow
+ anchors.bottom: slidersColumn.top
+ anchors.left: slidersColumn.left
+ }
+
+ PropertyChanges {
+ target: roboArm
+ scale.x: 0.7
+ scale.y: 0.7
+ scale.z: 0.7
+ y: 280
+ z: 100
+ }
+ }
+ ]
+
+ transitions: Transition {
+ PropertyAnimation {
+ properties: "sliderWidth, scale.x, scale.y, scale.z, y, z"
+ }
+ AnchorAnimation {}
+ }
+}
diff --git a/examples/demos/robotarm/content/NodeIndicator.qml b/examples/demos/robotarm/content/NodeIndicator.qml
new file mode 100644
index 00000000..9a3fc0c1
--- /dev/null
+++ b/examples/demos/robotarm/content/NodeIndicator.qml
@@ -0,0 +1,58 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Controls.Material
+import QtQuick3D
+import RobotArm
+
+Item {
+ id: root
+ property real size: 100
+ property bool isFocused: true
+ property View3D view3D: parent
+ property vector3d scenePosition
+ property vector3d screenPosition
+ property alias label: label.text
+
+ x: screenPosition.x
+ y: screenPosition.y
+
+ visible: x > 0 && y > 0
+
+ Label {
+ id: label
+ enabled: root.isFocused
+ anchors.bottom: rect.top
+ anchors.horizontalCenter: rect.horizontalCenter
+ }
+
+ Rectangle {
+ id: rect
+ width: root.size
+ height: root.size
+ color: "Transparent"
+ radius: width / 2
+ border.width: 2
+ border.color: root.isFocused ? Material.accentColor : Material.secondaryTextColor
+
+ anchors.horizontalCenter: parent.left
+ anchors.verticalCenter: parent.top
+ }
+
+ Component.onCompleted: {
+ screenPosition = Qt.binding(function() {
+ let w = view3D.width // force the binding to update when width or height changes
+ let h = view3D.height
+
+ return view3D.mapFrom3DScene(scenePosition)
+ } )
+ }
+}
+
+/*##^##
+Designer {
+ D{i:0;autoSize:true;height:480;width:640}
+}
+##^##*/
diff --git a/examples/demos/robotarm/content/RoboticArm.ui.qml b/examples/demos/robotarm/content/RoboticArm.ui.qml
new file mode 100644
index 00000000..c8554de6
--- /dev/null
+++ b/examples/demos/robotarm/content/RoboticArm.ui.qml
@@ -0,0 +1,163 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick3D
+
+Node {
+ id: rootNode
+ property int rotation1
+ property int rotation2
+ property int rotation3
+ property int rotation4
+ property int clawsAngle
+
+ readonly property alias hand_position: hand_grab_t.scenePosition
+ readonly property alias hand_hinge_position: hand_hinge.scenePosition
+ readonly property alias arm_position: arm.scenePosition
+ readonly property alias forearm_position: forearm.scenePosition
+ readonly property alias root_position: root.scenePosition
+
+ Model {
+ id: base
+ scale.x: 100
+ scale.y: 100
+ scale.z: 100
+ source: "meshes/base.mesh"
+ eulerRotation.x: -90
+
+ DefaultMaterial {
+ id: steel_material
+ diffuseColor: "#ff595959"
+ }
+
+ DefaultMaterial {
+ id: plastic_material
+ }
+ materials: [steel_material, plastic_material]
+
+ Model {
+ id: root
+ y: -5.96047e-08
+ z: 1.0472
+ eulerRotation.z: rotation4
+ source: "meshes/root.mesh"
+
+ DefaultMaterial {
+ id: plastic_color_material
+ diffuseColor: "#41cd52"
+ }
+ materials: [plastic_material, plastic_color_material, steel_material]
+
+ Model {
+ id: forearm
+ x: 5.32907e-15
+ y: -0.165542
+ z: 1.53472
+ eulerRotation.x: rotation3
+ source: "meshes/forearm.mesh"
+ materials: [plastic_material, steel_material]
+
+ Model {
+ id: arm
+ x: -7.43453e-07
+ y: 0.667101
+ z: 2.23365
+ eulerRotation.x: rotation2
+ source: "meshes/arm.mesh"
+
+ DefaultMaterial {
+ id: plastic_qt_material
+ diffuseMap: Texture {
+ source: "maps/qt.png"
+ pivotU: 0.5
+ pivotV: 0.5
+ generateMipmaps: true
+ mipFilter: Texture.Linear
+ }
+ }
+ materials: [plastic_material, plastic_qt_material, steel_material]
+
+ Model {
+ id: hand_hinge
+ x: 7.43453e-07
+ y: 0.0635689
+ z: 2.12289
+ eulerRotation.x: rotation1
+ source: "meshes/hand_hinge.mesh"
+ materials: [plastic_material]
+
+ Model {
+ id: hand
+ x: 3.35649e-06
+ y: 2.38419e-07
+ z: 0.366503
+ source: "meshes/hand.mesh"
+ materials: [plastic_material, steel_material]
+
+ Model {
+ id: hand_grab_t_hinge_2
+ x: -9.5112e-07
+ y: 0.323057
+ z: 0.472305
+ eulerRotation: hand_grab_t_hinge_1.eulerRotation
+ source: "meshes/hand_grab_t_hinge_2.mesh"
+ materials: [steel_material]
+ }
+
+ Model {
+ id: hand_grab_t_hinge_1
+ x: -9.3061e-07
+ y: 0.143685
+ z: 0.728553
+ eulerRotation.x: clawsAngle * -1
+ source: "meshes/hand_grab_t_hinge_1.mesh"
+ materials: [steel_material]
+
+ Model {
+ id: hand_grab_t
+ x: -2.42588e-06
+ y: -0.0327932
+ z: 0.414757
+ eulerRotation.x: hand_grab_t_hinge_1.eulerRotation.x * -1
+ source: "meshes/hand_grab_t.mesh"
+ materials: [plastic_color_material, steel_material]
+ }
+ }
+
+ Model {
+ id: hand_grab_b_hinge_1
+ x: -9.38738e-07
+ y: -0.143685
+ z: 0.728553
+ eulerRotation.x: clawsAngle
+ source: "meshes/hand_grab_b_hinge_1.mesh"
+ materials: [steel_material]
+
+ Model {
+ id: hand_grab_b
+ x: -2.41775e-06
+ y: 0.0327224
+ z: 0.413965
+ eulerRotation.x: hand_grab_b_hinge_1.eulerRotation.x * -1
+ source: "meshes/hand_grab_b.mesh"
+ materials: [plastic_color_material, steel_material]
+ }
+ }
+
+ Model {
+ id: hand_grab_b_hinge_2
+ x: -9.5112e-07
+ y: -0.323058
+ z: 0.472305
+ eulerRotation: hand_grab_b_hinge_1.eulerRotation
+ source: "meshes/hand_grab_b_hinge_2.mesh"
+ materials: [steel_material]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/examples/demos/robotarm/content/Toggle.ui.qml b/examples/demos/robotarm/content/Toggle.ui.qml
new file mode 100644
index 00000000..98812219
--- /dev/null
+++ b/examples/demos/robotarm/content/Toggle.ui.qml
@@ -0,0 +1,30 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+/*
+This is a UI file (.ui.qml) that is intended to be edited in Qt Design Studio only.
+It is supposed to be strictly declarative and only uses a subset of QML. If you edit
+this file manually, you might introduce QML code that is not supported by Qt Design Studio.
+Check out https://doc.qt.io/qtcreator/creator-quick-ui-forms.html for details on .ui.qml files.
+*/
+import QtQuick
+import QtQuick.Controls
+
+Item {
+ property string text
+ property alias checked: toggleIndicator.checked
+ readonly property alias hasFocus: toggleIndicator.activeFocus
+ implicitWidth: toggleText.width + toggleIndicator.width
+ implicitHeight: 50
+
+ Label {
+ id: toggleText
+ text: parent.text
+ anchors.verticalCenter: toggleIndicator.verticalCenter
+ }
+ Switch {
+ id: toggleIndicator
+ anchors.left: toggleText.right
+ anchors.rightMargin: 8
+ }
+}
diff --git a/examples/demos/robotarm/content/fonts/fonts.txt b/examples/demos/robotarm/content/fonts/fonts.txt
new file mode 100644
index 00000000..ab961220
--- /dev/null
+++ b/examples/demos/robotarm/content/fonts/fonts.txt
@@ -0,0 +1 @@
+Fonts in this folder are loaded automatically.
diff --git a/examples/demos/robotarm/content/maps/qt.png b/examples/demos/robotarm/content/maps/qt.png
new file mode 100644
index 00000000..45171461
--- /dev/null
+++ b/examples/demos/robotarm/content/maps/qt.png
Binary files differ
diff --git a/examples/demos/robotarm/content/meshes/arm.mesh b/examples/demos/robotarm/content/meshes/arm.mesh
new file mode 100644
index 00000000..b005076b
--- /dev/null
+++ b/examples/demos/robotarm/content/meshes/arm.mesh
Binary files differ
diff --git a/examples/demos/robotarm/content/meshes/base.mesh b/examples/demos/robotarm/content/meshes/base.mesh
new file mode 100644
index 00000000..f8506c85
--- /dev/null
+++ b/examples/demos/robotarm/content/meshes/base.mesh
Binary files differ
diff --git a/examples/demos/robotarm/content/meshes/forearm.mesh b/examples/demos/robotarm/content/meshes/forearm.mesh
new file mode 100644
index 00000000..690c577e
--- /dev/null
+++ b/examples/demos/robotarm/content/meshes/forearm.mesh
Binary files differ
diff --git a/examples/demos/robotarm/content/meshes/hand.mesh b/examples/demos/robotarm/content/meshes/hand.mesh
new file mode 100644
index 00000000..c87009e6
--- /dev/null
+++ b/examples/demos/robotarm/content/meshes/hand.mesh
Binary files differ
diff --git a/examples/demos/robotarm/content/meshes/hand_grab_b.mesh b/examples/demos/robotarm/content/meshes/hand_grab_b.mesh
new file mode 100644
index 00000000..c1784f8a
--- /dev/null
+++ b/examples/demos/robotarm/content/meshes/hand_grab_b.mesh
Binary files differ
diff --git a/examples/demos/robotarm/content/meshes/hand_grab_b_hinge_1.mesh b/examples/demos/robotarm/content/meshes/hand_grab_b_hinge_1.mesh
new file mode 100644
index 00000000..0c5686d5
--- /dev/null
+++ b/examples/demos/robotarm/content/meshes/hand_grab_b_hinge_1.mesh
Binary files differ
diff --git a/examples/demos/robotarm/content/meshes/hand_grab_b_hinge_2.mesh b/examples/demos/robotarm/content/meshes/hand_grab_b_hinge_2.mesh
new file mode 100644
index 00000000..cf7ced56
--- /dev/null
+++ b/examples/demos/robotarm/content/meshes/hand_grab_b_hinge_2.mesh
Binary files differ
diff --git a/examples/demos/robotarm/content/meshes/hand_grab_t.mesh b/examples/demos/robotarm/content/meshes/hand_grab_t.mesh
new file mode 100644
index 00000000..f9ebd265
--- /dev/null
+++ b/examples/demos/robotarm/content/meshes/hand_grab_t.mesh
Binary files differ
diff --git a/examples/demos/robotarm/content/meshes/hand_grab_t_hinge_1.mesh b/examples/demos/robotarm/content/meshes/hand_grab_t_hinge_1.mesh
new file mode 100644
index 00000000..3e7a42bc
--- /dev/null
+++ b/examples/demos/robotarm/content/meshes/hand_grab_t_hinge_1.mesh
Binary files differ
diff --git a/examples/demos/robotarm/content/meshes/hand_grab_t_hinge_2.mesh b/examples/demos/robotarm/content/meshes/hand_grab_t_hinge_2.mesh
new file mode 100644
index 00000000..3e7a42bc
--- /dev/null
+++ b/examples/demos/robotarm/content/meshes/hand_grab_t_hinge_2.mesh
Binary files differ
diff --git a/examples/demos/robotarm/content/meshes/hand_hinge.mesh b/examples/demos/robotarm/content/meshes/hand_hinge.mesh
new file mode 100644
index 00000000..7b0f0af0
--- /dev/null
+++ b/examples/demos/robotarm/content/meshes/hand_hinge.mesh
Binary files differ
diff --git a/examples/demos/robotarm/content/meshes/root.mesh b/examples/demos/robotarm/content/meshes/root.mesh
new file mode 100644
index 00000000..1501c2ed
--- /dev/null
+++ b/examples/demos/robotarm/content/meshes/root.mesh
Binary files differ
diff --git a/examples/demos/robotarm/doc/images/robotarm-example.png b/examples/demos/robotarm/doc/images/robotarm-example.png
new file mode 100644
index 00000000..c8d15839
--- /dev/null
+++ b/examples/demos/robotarm/doc/images/robotarm-example.png
Binary files differ
diff --git a/examples/demos/robotarm/doc/src/robotarm.qdoc b/examples/demos/robotarm/doc/src/robotarm.qdoc
new file mode 100644
index 00000000..98000bd6
--- /dev/null
+++ b/examples/demos/robotarm/doc/src/robotarm.qdoc
@@ -0,0 +1,23 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example demos/robotarm
+ \ingroup qtquickdemos
+ \title Robot Arm Example
+ \brief Demonstrates how to add a C++ backend to a 3D project from Qt Design Studio.
+ This example demonstrates adding a C++ backend to a 3D project created in Qt Design Studio. The example itself consists of
+ an interactive industrial robot arm in a Qt Quick 3D scene. The 2D UI to control the robot arm is implement using Qt Quick Controls.
+ \meta {tag} {demo,quick}
+ \image robotarm-example.png
+
+ For Qt Design Studio the Robot Arm Example comes with a simple QML based QML module in backend_moc/Backend/Backend_moc.qml,
+ that serves as a backend for the project when using it with Qt Design Studio.
+ The C++ application implements a compatible backend as a C++ based QML module. Both QML modules implement the same API, which ensures
+ compatibility between the two modules.
+
+ The Qt Quick 3D scene for the Robot Arm is defined in content/RoboticArm.ui.qml. The 2D UI is implemented in content/MainScreen.ui.qml
+ and is repsonsive and also supports a light and dark mode. The example uses the Material style from Qt Quick Controls and the dark and light
+ theme to implement both modes.
+
+*/
diff --git a/examples/demos/robotarm/imports/CMakeLists.txt b/examples/demos/robotarm/imports/CMakeLists.txt
new file mode 100644
index 00000000..649d3bbf
--- /dev/null
+++ b/examples/demos/robotarm/imports/CMakeLists.txt
@@ -0,0 +1,4 @@
+### This file is automatically generated by Qt Design Studio.
+### Do not change
+
+add_subdirectory(RobotArm)
diff --git a/examples/demos/robotarm/imports/RobotArm/CMakeLists.txt b/examples/demos/robotarm/imports/RobotArm/CMakeLists.txt
new file mode 100644
index 00000000..2636a8f9
--- /dev/null
+++ b/examples/demos/robotarm/imports/RobotArm/CMakeLists.txt
@@ -0,0 +1,16 @@
+### This file is automatically generated by Qt Design Studio.
+### Do not change
+
+qt_add_library(RobotArm STATIC)
+set_source_files_properties(Constants.qml
+ PROPERTIES
+ QT_QML_SINGLETON_TYPE true
+)
+
+qt6_add_qml_module(RobotArm
+ URI "RobotArm"
+ VERSION 1.0
+ QML_FILES
+ Constants.qml
+ RESOURCE_PREFIX "/"
+)
diff --git a/examples/demos/robotarm/imports/RobotArm/Constants.qml b/examples/demos/robotarm/imports/RobotArm/Constants.qml
new file mode 100644
index 00000000..65cfc6c4
--- /dev/null
+++ b/examples/demos/robotarm/imports/RobotArm/Constants.qml
@@ -0,0 +1,22 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma Singleton
+import QtQuick
+
+QtObject {
+ readonly property int width: 1280
+ readonly property int height: 720
+
+ /* Edit this comment to add your custom font */
+ readonly property font font: Qt.font({
+ family: Qt.application.font.family,
+ pixelSize: Qt.application.font.pixelSize
+ })
+ readonly property font largeFont: Qt.font({
+ family: Qt.application.font.family,
+ pixelSize: Qt.application.font.pixelSize * 1.6
+ })
+
+ readonly property color backgroundColor: "#c2c2c2"
+}
diff --git a/examples/demos/robotarm/imports/RobotArm/designer/plugin.metainfo b/examples/demos/robotarm/imports/RobotArm/designer/plugin.metainfo
new file mode 100644
index 00000000..9acd6b59
--- /dev/null
+++ b/examples/demos/robotarm/imports/RobotArm/designer/plugin.metainfo
@@ -0,0 +1,13 @@
+MetaInfo {
+ Type {
+ name: "RobotArm.EventListSimulator"
+ icon: ":/qtquickplugin/images/item-icon16.png"
+
+ Hints {
+ visibleInNavigator: true
+ canBeDroppedInNavigator: true
+ canBeDroppedInFormEditor: false
+ canBeDroppedInView3D: false
+ }
+ }
+}
diff --git a/examples/demos/robotarm/imports/RobotArm/qmldir b/examples/demos/robotarm/imports/RobotArm/qmldir
new file mode 100644
index 00000000..3b7ff7bd
--- /dev/null
+++ b/examples/demos/robotarm/imports/RobotArm/qmldir
@@ -0,0 +1,5 @@
+Module RobotArm
+singleton Constants 1.0 Constants.qml
+EventListSimulator 1.0 EventListSimulator.qml
+EventListModel 1.0 EventListModel.qml
+DirectoryFontLoader 1.0 DirectoryFontLoader.qml
diff --git a/examples/demos/robotarm/main.qml b/examples/demos/robotarm/main.qml
new file mode 100644
index 00000000..fc77418f
--- /dev/null
+++ b/examples/demos/robotarm/main.qml
@@ -0,0 +1,11 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+/* This file is generated and only relevant for integrating the project into a Qt 6 and cmake based
+C++ project. */
+
+import QtQuick
+import content
+
+App {
+}
diff --git a/examples/demos/robotarm/qmlmodules b/examples/demos/robotarm/qmlmodules
new file mode 100644
index 00000000..0b3c1fcc
--- /dev/null
+++ b/examples/demos/robotarm/qmlmodules
@@ -0,0 +1,23 @@
+### This file is automatically generated by Qt Design Studio.
+### Do not change
+
+qt6_add_qml_module(RobotArmApp
+ URI "Main"
+ VERSION 1.0
+ NO_PLUGIN
+ QML_FILES main.qml
+ RESOURCE_PREFIX "/"
+)
+
+add_subdirectory(content)
+add_subdirectory(imports)
+
+set(QML_IMPORT_PATH
+ ${CMAKE_BINARY_DIR}
+ CACHE STRING "")
+
+target_link_libraries(RobotArmApp PRIVATE
+ contentplugin
+ RobotArmplugin
+ backendmodule
+)
diff --git a/examples/demos/robotarm/qtquickcontrols2.conf b/examples/demos/robotarm/qtquickcontrols2.conf
new file mode 100644
index 00000000..164b7114
--- /dev/null
+++ b/examples/demos/robotarm/qtquickcontrols2.conf
@@ -0,0 +1,6 @@
+[Controls]
+Style=Material
+
+[Material]
+Theme=Light
+Accent=Green
diff --git a/examples/demos/robotarm/src/app_environment.h b/examples/demos/robotarm/src/app_environment.h
new file mode 100644
index 00000000..57407419
--- /dev/null
+++ b/examples/demos/robotarm/src/app_environment.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+/*
+ * This file is automatically generated by Qt Design Studio.
+ * Do not change.
+*/
+
+#include <QGuiApplication>
+
+void set_qt_environment()
+{
+ qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1");
+ qputenv("QT_ENABLE_HIGHDPI_SCALING", "0");
+ qputenv("QT_LOGGING_RULES", "qt.qml.connections=false");
+ qputenv("QT_QUICK_CONTROLS_CONF", ":/qtquickcontrols2.conf");
+}
diff --git a/examples/demos/robotarm/src/import_qml_plugins.h b/examples/demos/robotarm/src/import_qml_plugins.h
new file mode 100644
index 00000000..50ff7ca7
--- /dev/null
+++ b/examples/demos/robotarm/src/import_qml_plugins.h
@@ -0,0 +1,12 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+/*
+ * This file is automatically generated by Qt Design Studio.
+ * Do not change.
+*/
+
+#include <QtQml/qqmlextensionplugin.h>
+
+Q_IMPORT_QML_PLUGIN(contentPlugin)
+Q_IMPORT_QML_PLUGIN(RobotArmPlugin)
diff --git a/examples/demos/robotarm/src/main.cpp b/examples/demos/robotarm/src/main.cpp
new file mode 100644
index 00000000..45726947
--- /dev/null
+++ b/examples/demos/robotarm/src/main.cpp
@@ -0,0 +1,43 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+/*
+ * This file is automatically generated by Qt Design Studio.
+ * Do not change.
+*/
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+#include "app_environment.h"
+#include "import_qml_plugins.h"
+
+int main(int argc, char *argv[])
+{
+ set_qt_environment();
+
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ const QUrl url(u"qrc:Main/main.qml"_qs);
+ QObject::connect(
+ &engine,
+ &QQmlApplicationEngine::objectCreated,
+ &app,
+ [url](QObject *obj, const QUrl &objUrl) {
+ if (!obj && url == objUrl)
+ QCoreApplication::exit(-1);
+ },
+ Qt::QueuedConnection);
+
+ engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml");
+ engine.addImportPath(":/");
+
+ engine.load(url);
+
+ if (engine.rootObjects().isEmpty()) {
+ return -1;
+ }
+
+ return app.exec();
+}
diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp
index 99969ae6..177a383b 100644
--- a/tests/auto/quick/examples/tst_examples.cpp
+++ b/tests/auto/quick/examples/tst_examples.cpp
@@ -63,6 +63,7 @@ tst_examples::tst_examples()
excludedFiles << "examples/quick/shapes/content/main.qml"; // relies on resources
excludedFiles << "examples/quick/shapes/content/interactive.qml"; // relies on resources
excludedFiles << "examples/demos/addressbook/qml/main.qml"; // relies on resources
+ excludedFiles << "examples/demos/robotarm/main.qml"; // relies on custom import
#ifdef QT_NO_XMLPATTERNS
excludedDirs << "demos/twitter";