From 8b4960acd9b0d4b45fc9b9fb7e903985f77e2625 Mon Sep 17 00:00:00 2001
From: Burak Hancerli <burak.hancerli@qt.io>
Date: Fri, 16 Feb 2024 12:10:20 +0100
Subject: [PATCH] fix: trying to fix styling issues

---
 CMakeLists.txt         |   5 +-
 src/backend.cpp        |  86 +++++++++++++++++--------
 src/backend.h          |  58 ++++++++++-------
 src/main.cpp           |  24 +++----
 src/projectManager.cpp |   9 +++
 ui/AboutHeader.qml     |   9 +--
 ui/HomePage.qml        |   4 +-
 ui/main.qml            | 138 ++++++++++++++++++-----------------------
 8 files changed, 180 insertions(+), 153 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index e027714..6b615b4 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 63ffd26..b91f198 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 68198b7..b153bf7 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 2703572..ecdac6f 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 f479b48..4914010 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 882880f..f2a087d 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 6bfb4be..5d7c1a4 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 1e76caf..c2e89b7 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 {
-- 
GitLab