diff --git a/CMakeLists.txt b/CMakeLists.txt index e0277147598d42872b814222d9ec98ee9e07ae0c..6b615b4edb994d816e292fb1bc111f5215808719 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,13 +13,13 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package( QT NAMES Qt6 - COMPONENTS Core Widgets Quick Gui Qml Multimedia MultimediaWidgets Concurrent + COMPONENTS Core Widgets Quick Gui Qml Multimedia MultimediaWidgets Concurrent QuickControls2 REQUIRED ) find_package( Qt6 - COMPONENTS Core Widgets Quick Gui Qml Multimedia MultimediaWidgets Concurrent + COMPONENTS Core Widgets Quick Gui Qml Multimedia MultimediaWidgets Concurrent QuickControls2 REQUIRED ) @@ -49,6 +49,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Qml Qt6::GuiPrivate Qt6::Multimedia Qt6::MultimediaWidgets Qt6::Concurrent + Qt6::QuickControls2 ZXing::ZXing ) diff --git a/src/backend.cpp b/src/backend.cpp index 63ffd2678686a92731f5751548acb3f01ebc6727..b91f19899cd7cf8fe629cb73dd120941ba4bcc7c 100644 --- a/src/backend.cpp +++ b/src/backend.cpp @@ -35,6 +35,8 @@ #include <QMediaDevices> #include <QMessageBox> #include <QPermission> +#include <QQmlContext> +#include <QQuickStyle> #include <QSettings> #include <QSslSocket> #include <QVideoSink> @@ -61,29 +63,11 @@ Backend::Backend(QObject *parent) &ServiceConnector::downloadProgress, this, &Backend::downloadProgress); -} - -void Backend::initialize() -{ - const QRect screenGeometry = QGuiApplication::primaryScreen()->geometry(); - qDebug() << "Qt Design Viewer"; - qDebug() << "System information:"; - qDebug() << "-- Qt version: " << QT_VERSION_STR; - qDebug() << "-- OpenSSL support: " << QVariant(QSslSocket::supportsSsl()).toString(); - qDebug() << "-- Screen height: " << QString::number(screenGeometry.height()); - qDebug() << "-- Screen width: " << QString::number(screenGeometry.width()); -#ifdef QT_DEBUG - const QString buildType = "Debug"; -#else - const QString buildType = "Release"; -#endif - const QString buildInfo = QCoreApplication::applicationVersion() + "\nTechnology Preview - " - + QString(GIT_VERSION) + "\nQt " + QString(QT_VERSION_STR) + " - " - + buildType + " Build" + "\nQt Quick Components " - + QString(QT_QUICK_COMPONENTS_VERSION) + "\nOpenSSL support: " - + QVariant(QSslSocket::supportsSsl()).toString(); - emit buildInfoChanged(buildInfo); + // Initialize background update + connect(&m_backgroundTimer, &QTimer::timeout, this, &Backend::updateUserProjectList); + m_backgroundTimer.setInterval(1000 * 10); + enableBackgroundUpdate(updateInBackground()); if (userHash().isEmpty()) qDebug("User Hash is not registered. Scan QR code to register."); @@ -92,11 +76,27 @@ void Backend::initialize() updateUserProjectList(); } - // Initialize background update - connect(&m_backgroundTimer, &QTimer::timeout, this, &Backend::updateUserProjectList); - m_backgroundTimer.setInterval(1000 * 10); - enableBackgroundUpdate(updateInBackground()); - qDebug() << "Initialization complete"; +#ifdef QT_DEBUG + const QString buildType = "Debug"; +#else + const QString buildType = "Release"; +#endif + m_buildInfo = QCoreApplication::applicationVersion() + "\nTechnology Preview - " + + QString(GIT_VERSION) + "\nQt " + QString(QT_VERSION_STR) + " - " + buildType + + " Build" + "\nQt Quick Components " + QString(QT_QUICK_COMPONENTS_VERSION) + + "\nOpenSSL support: " + QVariant(QSslSocket::supportsSsl()).toString(); + + const QRect screenGeometry = QGuiApplication::primaryScreen()->geometry(); + qDebug() << "Qt Design Viewer"; + qDebug() << "System information:"; + qDebug() << "-- Qt version: " << QT_VERSION_STR; + qDebug() << "-- OpenSSL support: " << QVariant(QSslSocket::supportsSsl()).toString(); + qDebug() << "-- Screen height: " << QString::number(screenGeometry.height()); + qDebug() << "-- Screen width: " << QString::number(screenGeometry.width()); + + // initialize the main UI after a short delay + // because we need to make sure that the backend is fully initialized + QTimer::singleShot(100, this, &Backend::initMainUI); } void Backend::enableBackgroundUpdate(const bool &enabled) @@ -268,10 +268,42 @@ void Backend::openCamera() m_captureSession->camera()->start(); } +void Backend::initMainUI() +{ + qDebug() << "Initializing main UI"; + // qmlClearTypeRegistrations(); + // qputenv("QT_QUICK_CONTROLS_STYLE", QByteArray("Material")); + // qputenv("QT_QUICK_CONTROLS_MATERIAL_THEME", QByteArray("Light")); + QQuickStyle::setStyle("Material"); + m_view.reset(new QQuickView(), [](QQuickView *v) { v->deleteLater(); }); + m_view->engine()->rootContext()->setContextProperty("backend", this); + m_view->setSource(QUrl(QStringLiteral("qrc:/main.qml"))); + m_view->setResizeMode(QQuickView::SizeRootObjectToView); + m_view->showMaximized(); + connect(m_view.data(), &QQuickView::destroyed, this, [&] { + qDebug() << "Main UI is destroyed."; + }); +} + +void Backend::clearMainUI() +{ + qDebug() << "Clearing main UI"; + m_view.reset(); +} + void Backend::initializeProjectManager() { + // Workaround for QDS-11413 + clearMainUI(); + // End of workaround + m_projectManager.reset(new ProjectManager(autoScaleProject())); + connect(m_projectManager.data(), &ProjectManager::closingProject, this, [&] { + qDebug() << "Project is closed"; + // workaround for QDS-11413 + initMainUI(); + // end of workaround emit popupClose(); m_projectManager.reset(); }); diff --git a/src/backend.h b/src/backend.h index 68198b7d85336ecedb4bfa52e1d5ea7e58bddf7e..b153bf7bc10d3f737f80fce33f5a6730072852eb 100644 --- a/src/backend.h +++ b/src/backend.h @@ -59,10 +59,11 @@ class Backend : public QObject { Q_OBJECT Q_PROPERTY(QJsonArray projectList READ projectList NOTIFY projectListChanged) + Q_PROPERTY( + int currentStackLayoutIndex READ currentStackLayoutIndex WRITE setCurrentStackLayoutIndex) public: explicit Backend(QObject *parent = nullptr); - void initialize(); void setLogs(const QString &logs) { m_logs = logs; @@ -70,11 +71,18 @@ public: } QJsonArray projectList() const { return m_projectList; } + int currentStackLayoutIndex() const { return m_currentStackLayoutIndex; } + void setCurrentStackLayoutIndex(const int &index) { m_currentStackLayoutIndex = index; } private: + // main UI + QSharedPointer<QQuickView> m_view; + // UI data QString m_logs; QJsonArray m_projectList; + QString m_buildInfo; + int m_currentStackLayoutIndex = 0; // Other members ServiceConnector m_serviceConnector; @@ -91,29 +99,9 @@ private: // member functions void updatePopup(const QString &text, bool indeterminate = true); -signals: - // UI signals - Home page - void projectListChanged(); - void urlUpdated(QString); - void userHashChanged(); - - // UI signals - Logs page - void logsChanged(QString); - - // UI signals - About page - void buildInfoChanged(QString); - - // UI signals - Popup - void downloadProgress(float); - void popupProgressIndeterminateChanged(bool indeterminate); - void popupTextChanged(QString text); - void popupOpen(); - void popupClose(); - - // UI signals - Network page - void networkUpdated(QString); - public slots: + QString buildInfo() const { return m_buildInfo; } + void scanQrCode(); void openCamera(); @@ -140,6 +128,30 @@ private slots: void initializeProjectManager(); void enableBackgroundUpdate(const bool &enabled); void updateUserProjectList(); + void initMainUI(); + void clearMainUI(); + +signals: + // UI signals - Home page + void projectListChanged(); + void urlUpdated(QString); + void userHashChanged(); + + // UI signals - Logs page + void logsChanged(QString); + + // UI signals - Popup + void downloadProgress(float); + void popupProgressIndeterminateChanged(bool indeterminate); + void popupTextChanged(QString text); + void popupOpen(); + void popupClose(); + + // UI signals - Network page + void networkUpdated(QString); + + // other signals + void resetQmlEngine(); }; #endif // DV_ANDROID_H diff --git a/src/main.cpp b/src/main.cpp index 270357215b77f6eb4694d92ef6016ac32fc194d9..ecdac6f4deabea3f49ad5eaf4d4f06f7d72bff9f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,7 +27,6 @@ #include <QApplication> #include <QMessageBox> -#include <QQmlContext> #include "backend.h" @@ -64,33 +63,28 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt newLog += logPrefix + localMsg + logSuffix + "\n"; __android_log_print(ANDROID_LOG_DEBUG, "Qt_UI_Viewer", "%s", qPrintable(newLog)); - if (type == QtWarningMsg) { - if (backend) - backend->setLogs(appLogs += newLog); - } else if (type == QtCriticalMsg || type == QtFatalMsg) { - QMessageBox msgBox{QMessageBox::Critical, "Critical:", msg, QMessageBox::Ok}; - msgBox.exec(); + if (type != QtDebugMsg) { if (backend) backend->setLogs(appLogs += newLog); + + if (type == QtCriticalMsg || type == QtFatalMsg) { + QMessageBox msgBox{QMessageBox::Critical, "Critical:", msg, QMessageBox::Ok}; + msgBox.exec(); + } } } int main(int argc, char *argv[]) { qInstallMessageHandler(messageHandler); + + qDebug() << "Starting Qt Design Viewer"; + QApplication app(argc, argv); QApplication::setOrganizationName("Qt"); QApplication::setApplicationName(QStringLiteral("Qt Design Viewer")); QApplication::setApplicationVersion(QString("Built on %1 %2").arg(__DATE__, __TIME__)); - QQuickView view; backend = new Backend(); - - view.engine()->rootContext()->setContextProperty("backend", backend); - view.setSource(QUrl(QStringLiteral("qrc:/main.qml"))); - view.setResizeMode(QQuickView::SizeRootObjectToView); - view.showMaximized(); - - backend->initialize(); return app.exec(); } diff --git a/src/projectManager.cpp b/src/projectManager.cpp index f479b48db4bf23f6246feb7fa863fd867abb1583..49140103e10ed835102cdd565aff11055e4a6bd5 100644 --- a/src/projectManager.cpp +++ b/src/projectManager.cpp @@ -34,6 +34,7 @@ #include <QGuiApplication> #include <QJsonDocument> #include <QQuickItem> +#include <QQuickStyle> #include <QRandomGenerator> #include <QRegularExpression> #include <QResource> @@ -240,6 +241,14 @@ bool ProjectManager::runProject(const QString &projectPath) } qDebug() << "Initializing the qmlEngine"; + + // This is a workaround for QDS-11413 + // The QML engines are not isolated from each other, so we need to clear the type registrations + // But that causes a crash in the main QML engine that we use for the main UI (initialized in main.cpp), + // so we need to reset the main engine as well. + // qmlClearTypeRegistrations(); + // qputenv("QT_QUICK_CONTROLS_STYLE", QByteArray("Basic")); + QQuickStyle::setStyle("Basic"); m_qmlEngine.reset(new QQmlEngine); m_qmlEngine->clearComponentCache(); diff --git a/ui/AboutHeader.qml b/ui/AboutHeader.qml index 882880f94c8d0fd98925778879109e4dd7eddc68..f2a087d93b86877eff346d3c03599009ab7655de 100644 --- a/ui/AboutHeader.qml +++ b/ui/AboutHeader.qml @@ -22,7 +22,7 @@ Item { id: logs Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter horizontalAlignment: "AlignHCenter" - text: qsTr("Hello World") + text: backend.buildInfo() } Item { @@ -30,12 +30,5 @@ Item { Layout.fillHeight: true Layout.fillWidth: true } - - Connections { - target: backend - function onBuildInfoChanged(buildInfo){ - logs.text = buildInfo; - } - } } } diff --git a/ui/HomePage.qml b/ui/HomePage.qml index 6bfb4bed5bb8e312eac8aad5fb36280690848145..5d7c1a4de8d09a4fdddef7cac52255d5eb920a8d 100644 --- a/ui/HomePage.qml +++ b/ui/HomePage.qml @@ -87,7 +87,9 @@ Item { Button { id: downloadUserProject text: qsTr("Run Project") - onClicked: backend.runUserProject(projectList.currentText) + onClicked: { + backend.runUserProject(projectList.displayText); + } enabled: false Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter diff --git a/ui/main.qml b/ui/main.qml index 1e76caf38d0a9965ac7c111d87815ca7ae75d553..c2e89b7020188fdaeee10f9776525184fa5047b6 100644 --- a/ui/main.qml +++ b/ui/main.qml @@ -10,7 +10,6 @@ Rectangle { color: "#EAEAEA" state: "vertical" - M.Material.theme: M.Material.Light M.Material.accent: M.Material.Blue M.Material.primary: M.Material.Blue @@ -88,6 +87,8 @@ Rectangle { anchors.rightMargin: 20 anchors.leftMargin: 20 anchors.topMargin: 10 + currentIndex: backend.currentStackLayoutIndex + onCurrentIndexChanged: backend.currentStackLayoutIndex = currentIndex HomePage { id: homePage @@ -158,107 +159,90 @@ Rectangle { id: scrollview anchors.fill: parent - ColumnLayout { - id: column - anchors.fill: drawer - anchors.rightMargin: 10 - anchors.leftMargin: 10 - width: Math.max(implicitWidth, drawer.availableWidth) - height: Math.max(implicitHeight, drawer.availableHeight) - - TabButton { - id: home - text: qsTr("Home") - Layout.fillWidth: true - checked: true - checkable: true - autoExclusive: true - - Connections { - target: home - function onClicked(){ + ColumnLayout { + id: column + anchors.fill: drawer + anchors.rightMargin: 10 + anchors.leftMargin: 10 + width: Math.max(implicitWidth, drawer.availableWidth) + height: Math.max(implicitHeight, drawer.availableHeight) + onActiveFocusChanged: if (!activeFocus) drawer.close() + + TabButton { + id: home + text: qsTr("Home") + Layout.fillWidth: true + checked: true + checkable: true + autoExclusive: true + onClicked: { stackLayout.currentIndex = 0 + drawer.close() } } - } - TabButton { - id: examples - text: qsTr("Examples") - Layout.fillWidth: true - checkable: true - autoExclusive: true - - Connections { - target: examples - function onClicked(){ + TabButton { + id: examples + text: qsTr("Examples") + Layout.fillWidth: true + checkable: true + autoExclusive: true + onClicked: { stackLayout.currentIndex = 1 + drawer.close() } } - } - - TabButton { - id: logs - text: qsTr("Logs") - Layout.fillWidth: true - checkable: true - autoExclusive: true - Connections { - target: logs - function onClicked(){ + TabButton { + id: logs + text: qsTr("Logs") + Layout.fillWidth: true + checkable: true + autoExclusive: true + onClicked: { stackLayout.currentIndex = 2 + drawer.close() } } - } - - TabButton { - id: network - text: qsTr("Network") - Layout.fillWidth: true - checkable: true - autoExclusive: true - visible: true; - Connections { - target: network - function onClicked(){ + TabButton { + id: network + text: qsTr("Network") + Layout.fillWidth: true + checkable: true + autoExclusive: true + visible: true; + onClicked: { stackLayout.currentIndex = 3 + drawer.close() } } - } - - TabButton { - id: settings - text: qsTr("Settings") - Layout.fillWidth: true - checkable: true - autoExclusive: true - Connections { - target: settings - function onClicked(){ + TabButton { + id: settings + text: qsTr("Settings") + Layout.fillWidth: true + checkable: true + autoExclusive: true + onClicked: { stackLayout.currentIndex = 4 + drawer.close() } } - } - TabButton { - id: about - text: qsTr("About") - Layout.fillWidth: true - checkable: true - autoExclusive: true - - Connections { - target: about - function onClicked(){ + TabButton { + id: about + text: qsTr("About") + Layout.fillWidth: true + checkable: true + autoExclusive: true + onClicked: { stackLayout.currentIndex = 5 + drawer.close() } } } } - } } Button {