Skip to content
Snippets Groups Projects
Commit f9813bcd authored by Burak Hançerli's avatar Burak Hançerli :headphones:
Browse files

QDS-11332 Implement better qmlproject handling

parent e46387e7
No related branches found
No related tags found
1 merge request!22QDS-11332 Implement better qmlproject handling
Pipeline #64612 passed
......@@ -34,6 +34,7 @@ qt_add_executable(${PROJECT_NAME}
src/backend.cpp src/backend.h
src/serviceConnector.cpp src/serviceConnector.h
src/projectManager.cpp src/projectManager.h
src/dsConnector.cpp src/dsConnector.h
ui/main.qml
ui/resources.qrc
)
......
<?xml version="1.0"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.qt.qtuiviewer"
android:installLocation="auto" android:versionCode="15" android:versionName="1.2">
android:installLocation="auto" android:versionCode="16" android:versionName="1.2">
<!-- %%INSERT_PERMISSIONS -->
<!-- %%INSERT_FEATURES -->
<supports-screens android:anyDensity="true" android:largeScreens="true"
......
......@@ -32,7 +32,7 @@
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonObject>
#include <QMessageBox>
#include <QSettings>
#include <QSslSocket>
......@@ -79,14 +79,8 @@ void Backend::initialize()
&ProjectManager::projectStateChanged,
this,
&Backend::popupTextChanged);
qDebug("Initialization complete");
}
void Backend::showWarning(const QString &message)
{
QMessageBox msg(QMessageBox::Warning, "Warning", message, QMessageBox::Ok);
msg.exec();
qWarning() << message;
qDebug("Initialization complete");
}
void Backend::updatePopup(const QString &text, bool indeterminate)
......@@ -96,6 +90,92 @@ void Backend::updatePopup(const QString &text, bool indeterminate)
QEventLoop().processEvents(QEventLoop::AllEvents, 100);
}
bool Backend::connectDesignStudio()
{
if (m_designStudioConnector) {
qDebug() << "Design Studio Connector is already initialized";
return true;
}
qDebug() << "Initializing Design Studio Connector";
connect(
&m_dsConnectorThread,
&QThread::started,
this,
[&] {
m_designStudioConnector.reset(new DesignStudioConnector);
connect(m_designStudioConnector.data(),
&DesignStudioConnector::networkStatusUpdated,
this,
&Backend::networkUpdated);
connect(m_designStudioConnector.data(),
&DesignStudioConnector::projectReceived,
&m_projectManager,
[this](const QByteArray &projectData) {
qDebug() << "Project received from Design Studio";
emit popupOpen();
updatePopup("Unpacking project...");
qDebug() << "Project data size: " << projectData.size();
const QString projectPath = m_projectManager.unpackProject(projectData);
if (projectPath.isEmpty()) {
qCritical()
<< "Could not unpack project. Please check the logs for more "
"information.";
emit popupClose();
return;
}
qDebug() << "Project unpacked to " << projectPath;
updatePopup("Running project...");
if (!m_projectManager.runProject(projectPath)) {
qCritical() << "Could not run project. Please check the logs for more "
"information.";
} else {
m_projectManager.showAppWindow();
}
emit popupClose();
});
if (!m_designStudioConnector->initialize()) {
qCritical()
<< "Could initialize server. Please check the logs for more information.";
m_designStudioConnector.reset();
}
},
Qt::DirectConnection);
m_dsConnectorThread.start();
qDebug() << "Design Studio Connector is initialized";
return true;
}
void Backend::disconnectDesignStudio()
{
if (!m_designStudioConnector) {
qDebug() << "Design Studio Connector is not initialized";
return;
}
qDebug() << "Disconnecting from Design Studio";
connect(
&m_dsConnectorThread,
&QThread::finished,
this,
[&] {
qDebug() << "Design Studio Connector is disconnected";
m_designStudioConnector.reset();
},
Qt::DirectConnection);
m_dsConnectorThread.quit();
m_dsConnectorThread.wait();
}
void Backend::runDemoProject(const QString &projectName)
{
qDebug() << "Checking if demo project is cached for " << projectName;
......@@ -112,8 +192,8 @@ void Backend::runDemoProject(const QString &projectName)
updatePopup("Caching demo project...");
if (!m_projectManager.cacheDemoProject(project, projectName)) {
showWarning(
"Could not cache demo project. Please check the logs for more information.");
qCritical()
<< "Could not cache demo project. Please check the logs for more information.";
emit popupClose();
return;
}
......@@ -123,7 +203,7 @@ void Backend::runDemoProject(const QString &projectName)
updatePopup("Running demo project...");
if (!m_projectManager.runDemoProject(projectName))
showWarning("Could not run demo project. Please check the logs for more information.");
qCritical() << "Could not run demo project. Please check the logs for more information.";
else
m_projectManager.showAppWindow();
......@@ -165,7 +245,7 @@ void Backend::runUserProject(const QString &url)
updatePopup("Caching user project...");
if (!m_projectManager.cacheProject(projectData, projectInfo)) {
showWarning("Could not cache project. Please check the logs for more information.");
qCritical() << "Could not cache project. Please check the logs for more information.";
emit popupClose();
return;
}
......@@ -175,7 +255,7 @@ void Backend::runUserProject(const QString &url)
updatePopup("Running cached project...");
if (!m_projectManager.runCachedProject(projectInfo))
showWarning("Could not run project. Please check the logs for more information.");
qCritical() << "Could not run project. Please check the logs for more information.";
else
m_projectManager.showAppWindow();
......
......@@ -26,6 +26,9 @@
#ifndef DV_ANDROID_H
#define DV_ANDROID_H
#include <QThread>
#include "dsConnector.h"
#include "projectManager.h"
#include "serviceConnector.h"
......@@ -57,9 +60,10 @@ private:
QString m_userHash;
ServiceConnector m_serviceConnector;
ProjectManager m_projectManager;
QScopedPointer<DesignStudioConnector> m_designStudioConnector;
QThread m_dsConnectorThread;
// member functions
void showWarning(const QString &message);
void updateUserProjectList();
void updatePopup(const QString &text, bool indeterminate = true);
......@@ -74,12 +78,15 @@ signals:
void popupOpen();
void popupClose();
void userRegistered();
void networkUpdated(QString);
public slots:
void runUserProject(const QString &url);
void runDemoProject(const QString &projectName);
void clearDemoCaches();
void registerUser(const QUrl &url);
bool connectDesignStudio();
void disconnectDesignStudio();
};
#endif // DV_ANDROID_H
/****************************************************************************
**
** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Design Viewer of the Qt Toolkit.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "dsConnector.h"
#include <QNetworkInterface>
void DesignStudioConnector::receiveProject()
{
QByteArray data = m_tcpSocket->readAll();
// qDebug() << "Data from Design Studio:" << data;
if (data.startsWith("qres")) {
qDebug() << "TCP:: Received project start delimeter";
m_projectData.clear();
m_projectData.append(data);
m_receivingData = true;
} else if (data.contains("::qmlrc-end::")) {
qDebug() << "TCP:: Received project end delimeter";
// qDebug() << "TCP:: Last data sequence: " << data;
m_projectData.append(data.mid(0, data.size() - 13));
emit projectReceived(m_projectData);
m_receivingData = false;
} else if (m_receivingData) {
qDebug() << "TCP:: Received data sequence";
m_projectData.append(data);
} else {
qDebug() << "TCP:: Received unknown data:" << data;
}
}
bool DesignStudioConnector::initTcpServer()
{
const bool retVal = m_tcpServer.listen(QHostAddress::Any, m_tcpPort);
if (!retVal) {
qDebug() << "Failed to listen on port " << m_tcpPort;
emit networkStatusUpdated("Failed to bind on port " + QString::number(m_tcpPort));
return false;
}
qDebug() << "Listening on port " << m_tcpPort;
connect(&m_tcpServer, &QTcpServer::newConnection, this, [this]() {
qDebug() << "New connection from Design Studio";
emit networkStatusUpdated("Design Studio is connected.\n Waiting for project...");
m_tcpSocket.reset(m_tcpServer.nextPendingConnection());
m_broadcastTimer.stop();
connect(m_tcpSocket.data(), &QTcpSocket::disconnected, this, [this]() {
qDebug() << "Disconnected from Design Studio";
emit networkStatusUpdated("\nLocal IP: " + m_ipv4Addr
+ "\nWaiting for Design Studio to connect...");
m_broadcastTimer.start();
m_projectData.clear();
});
connect(m_tcpSocket.data(),
&QTcpSocket::readyRead,
this,
&DesignStudioConnector::receiveProject);
});
return true;
}
void DesignStudioConnector::initBroadcast()
{
// get ipv4 address
const QList<QHostAddress> list = QNetworkInterface::allAddresses();
for (const QHostAddress &address : list) {
if (address.protocol() == QAbstractSocket::IPv4Protocol
&& address != QHostAddress::LocalHost) {
qDebug() << "Found IPv4 address:" << address.toString();
m_ipv4Addr = address.toString();
break;
}
}
qDebug() << "Advertising from" << m_ipv4Addr;
emit networkStatusUpdated("\nLocal IP: " + m_ipv4Addr
+ "\nWaiting for Design Studio to connect...");
// Broadcast a message to the network in each 2 seconds
// to let the Design Studio know that we are here
connect(&m_broadcastTimer, &QTimer::timeout, this, [this]() {
QByteArray datagram = "qt_ui_viewer_at:" + m_ipv4Addr.toUtf8() + ":"
+ QByteArray::number(m_tcpPort);
m_udpSocket.writeDatagram(datagram.data(),
datagram.size(),
QHostAddress::Broadcast,
m_udpPort);
qDebug() << "Broadcasting datagram:" << datagram;
});
m_broadcastTimer.setSingleShot(false);
m_broadcastTimer.start(2000);
}
bool DesignStudioConnector::initialize()
{
if (!initTcpServer())
return false;
initBroadcast();
return true;
}
/****************************************************************************
**
** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Design Viewer of the Qt Toolkit.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#ifndef DSCONNECTOR_H
#define DSCONNECTOR_H
#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTimer>
#include <QUdpSocket>
class DesignStudioConnector : public QObject
{
Q_OBJECT
public:
explicit DesignStudioConnector(QObject *parent = nullptr)
: QObject(parent)
{}
bool initialize();
private:
// Tcp connection members
QTcpServer m_tcpServer;
QScopedPointer<QTcpSocket> m_tcpSocket;
const quint32 m_tcpPort = 40000;
// Udp connection members
QUdpSocket m_udpSocket;
QTimer m_broadcastTimer;
QString m_ipv4Addr;
const quint32 m_udpPort = 45000;
// Other members
QByteArray m_projectData;
bool m_receivingData;
// Member functions
bool initTcpServer();
void initBroadcast();
void receiveProject();
signals:
void networkStatusUpdated(QString);
void projectReceived(QByteArray);
};
#endif // DSCONNECTOR_H
......@@ -25,6 +25,7 @@
#include <android/log.h>
#include <QApplication>
#include <QMessageBox>
#include <QQmlContext>
#include "backend.h"
......@@ -38,7 +39,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt
const char *file = context.file ? context.file : "";
const char *function = context.function ? context.function : "";
QString logPrefix, logSuffix, newLog;
QMessageBox msgBox(QMessageBox::Critical, "Critical:", msg, QMessageBox::Ok);
switch (type)
{
case QtDebugMsg:
......@@ -52,12 +53,14 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt
break;
case QtCriticalMsg:
logPrefix = QStringLiteral("Critical: ");
msgBox.exec();
break;
case QtFatalMsg:
logPrefix = QStringLiteral("Fatal: ");
logSuffix = QStringLiteral(" (%1:%2, %3)").arg(file).arg(context.line).arg(function);
break;
}
newLog += logPrefix + localMsg + logSuffix + "\n";
__android_log_print(ANDROID_LOG_DEBUG, "Qt_UI_Viewer", "%s", qPrintable(newLog));
......
......@@ -42,8 +42,6 @@
#include <QTemporaryFile>
#include <QtGui/private/qzipreader_p.h>
#define QSTRN QString::number
ProjectManager::ProjectManager(QObject *parent)
: QObject(parent)
, m_projectCachePath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))
......@@ -90,7 +88,7 @@ QString ProjectManager::unpackProject(const QByteArray &project, bool extractZip
const uchar *data;
if (m_projectData.size()) {
qDebug() << "Unregistering the previous data from QRC system. Path: " << m_projectPath
<< " Size: " << QString::number(m_projectData.size()) << " bytes.";
<< " Size: " << m_projectData.size() << " bytes.";
data = reinterpret_cast<const uchar *>(m_projectData.data());
if (!QResource::unregisterResource(data, m_projectPath)) {
qCritical("Cannot unregister the previous resource data.");
......@@ -99,7 +97,7 @@ QString ProjectManager::unpackProject(const QByteArray &project, bool extractZip
m_projectData = project;
data = reinterpret_cast<const uchar *>(m_projectData.data());
qDebug() << "Registering resource data. Size: " << QString::number(m_projectData.size());
qDebug() << "Registering resource data. Size: " << m_projectData.size();
const QString resourcePath{"/" + QString::number(QRandomGenerator::global()->generate())};
m_projectPath = resourcePath;
......@@ -120,114 +118,110 @@ QString ProjectManager::unpackProject(const QByteArray &project, bool extractZip
QString ProjectManager::findFile(const QString &dir, const QString &filter)
{
QDirIterator it(dir, {filter}, QDir::Files, QDirIterator::Subdirectories);
QDirIterator it(dir, {filter}, QDir::Files);
return it.next();
}
void ProjectManager::parseQmlProjectFile(const QString &fileName,
QString *mainFile,
QStringList *importPaths)
QString ProjectManager::readQmlProjectFile(const QString &qmlProjectFilePath)
{
/* if filename comes from a resource, then qml need qrc:/ at the mainfile and importPaths.
* But all other c++ call like QFileInfo::exists do not understand that, there we
* need to keep the only ":" character at the beginning of the string
*/
QFile file(fileName);
QFile file(qmlProjectFilePath);
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Could not open Qml Project file! " << fileName + ": " << file.errorString();
return;
qCritical() << "Could not open Qml Project file! " << file.fileName() + ": "
<< file.errorString();
return {};
}
const QString text = QString::fromUtf8(file.readAll());
return QString::fromUtf8(file.readAll());
}
QString ProjectManager::getMainQmlFile(const QString &projectPath,
const QString &qmlProjectFileContent)
{
const QRegularExpression mainFileRegExp("mainFile:\\s*\"(.*)\"");
const QRegularExpressionMatch mainFileMatch = mainFileRegExp.match(text);
const QRegularExpressionMatch mainFileMatch = mainFileRegExp.match(qmlProjectFileContent);
if (!mainFileMatch.hasMatch()) {
qCritical() << "No main file found in " << fileName;
return;
qCritical() << "No main file found in " << qmlProjectFileContent;
return {};
}
qDebug() << "Found main file: " << mainFileMatch.captured(1);
QString basePath = QFileInfo(fileName).path() + "/";
return projectPath + "/" + mainFileMatch.captured(1);
}
*mainFile = basePath + mainFileMatch.captured(1);
if (mainFile->startsWith(QLatin1String(":/")))
*mainFile = "qrc:" + mainFile->mid(1);
QStringList ProjectManager::getImportPaths(const QString &projectPath,
const QString &qmlProjectFileContent)
{
const QRegularExpression importPathsRegExp("importPaths:\\s*\\[\\s*(.*)\\s*\\]");
const QRegularExpressionMatch importPathsMatch = importPathsRegExp.match(qmlProjectFileContent);
const QRegularExpression qt6ProjectRegExp("qt6Project:\\s*true");
const QRegularExpressionMatch qt6ProjectMatch = qt6ProjectRegExp.match(text);
if (!qt6ProjectMatch.hasMatch()) {
qWarning("This is not a Qt6 project.\nQt5 projects might work, but they are not "
"officially supported.");
if (!importPathsMatch.hasMatch()) {
qWarning() << "No import paths found in " << qmlProjectFileContent;
return {};
}
const QRegularExpression importPathsRegExp("importPaths:\\s*\\[\\s*(.*)\\s*\\]");
const QRegularExpressionMatch importPathsMatch = importPathsRegExp.match(text);
if (importPathsMatch.hasMatch()) {
for (const QString &path : importPathsMatch.captured(1).split(",")) {
QString cleanedPath = path.trimmed();
cleanedPath = basePath + cleanedPath.mid(1, cleanedPath.length() - 2);
if (QFileInfo::exists(cleanedPath)) {
if (cleanedPath.startsWith(QLatin1String(":/")))
cleanedPath = "qrc:" + cleanedPath.mid(1);
importPaths->append(cleanedPath);
}
QStringList importPaths;
for (const QString &path : importPathsMatch.captured(1).split(",")) {
QString cleanedPath = path.trimmed();
cleanedPath = projectPath + "/" + cleanedPath.mid(1, cleanedPath.length() - 2);
if (QFileInfo::exists(cleanedPath)) {
importPaths.append(cleanedPath);
}
}
return importPaths;
}
bool ProjectManager::isQt6Project(const QString &qmlProjectFileContent)
{
const QRegularExpression qt6ProjectRegExp("qt6Project:\\s*true");
const QRegularExpressionMatch qt6ProjectMatch = qt6ProjectRegExp.match(qmlProjectFileContent);
return qt6ProjectMatch.hasMatch();
}
bool ProjectManager::runProject(const QString &projectPath, const QString &projectName)
bool ProjectManager::runProject(const QString &projectPath)
{
const QString newProjectName = QString(projectName).remove(".qmlrc").remove("#");
qDebug() << "Project location: " << projectPath;
qDebug() << "Project name: " << projectName;
qDebug() << "New project name: " << newProjectName;
QString mainQmlFilePath;
QStringList importPaths;
const QString qmlProjectFile = findFile(projectPath, "*.qmlproject");
if (qmlProjectFile.isEmpty()) {
qCritical() << "No \"*.qmlproject\" found in \"" << projectPath << "\".";
return false;
}
qDebug() << "Looking for qmlproject file in " << projectPath;
const QString qmlProjectFile = findFile(projectPath, newProjectName + "*.qmlproject");
if (!qmlProjectFile.isEmpty()) {
qDebug() << "Found qmlproject file: " << qmlProjectFile;
parseQmlProjectFile(qmlProjectFile, &mainQmlFilePath, &importPaths);
} else {
qWarning("Not found: \"*.qmlproject\". Looking for main.qml..");
mainQmlFilePath = findFile(projectPath, "main.qml");
const QString qmlProjectFileContent = readQmlProjectFile(qmlProjectFile);
if (qmlProjectFileContent.isEmpty())
return false;
if (mainQmlFilePath.isEmpty()) {
qWarning() << "Not found: \"main.qml\". Looking for \"" << newProjectName << ".qml\"..";
mainQmlFilePath = findFile(projectPath, newProjectName + ".qml");
}
const QString mainQmlFilePath = getMainQmlFile(projectPath, qmlProjectFileContent);
if (mainQmlFilePath.isEmpty())
return false;
const QString qtquickcontrols2File = findFile(projectPath, "qtquickcontrols2.conf");
if (!qtquickcontrols2File.isEmpty()) {
qputenv("QT_QUICK_CONTROLS_CONF", qtquickcontrols2File.toLatin1());
}
if (mainQmlFilePath.isEmpty()) {
qCritical() << "No \"*.qmlproject\", \"main.qml\" or \"" << newProjectName
<< ".qml\" found in \"" << projectPath << "\".";
return false;
const QStringList importPaths = getImportPaths(projectPath, qmlProjectFileContent);
if (isQt6Project(qmlProjectFileContent)) {
qWarning() << "This is not a Qt6 project.\nQt5 projects might work, but they are not "
"officially supported.";
}
qDebug() << "Found mainQmlFile: " + mainQmlFilePath;
qDebug() << "Project is parsed successfully.";
qDebug() << "Qml Project File: " << qmlProjectFile;
qDebug() << "Main Qml File: " << mainQmlFilePath;
qDebug() << "Import Paths: " << importPaths;
qDebug() << "Qt Quick Controls 2 File: " << qtquickcontrols2File;
QUrl mainQmlUrl = QUrl::fromUserInput(mainQmlFilePath);
QFile file(mainQmlUrl.path());
// QFile file(mainQmlUrl.path().prepend(":"));
if (!file.open(QIODevice::ReadOnly)) {
qCritical() << "Could not open mainQmlfile for reading! " << file.fileName() << ": "
<< file.errorString();
return false;
}
qDebug() << "Looking for qtquickcontrols2File in " << projectPath;
const QString qtquickcontrols2File = findFile(projectPath, "qtquickcontrols2.conf");
if (!qtquickcontrols2File.isEmpty()) {
qDebug() << "Found qtquickcontrols2File: " << qtquickcontrols2File;
qputenv("QT_QUICK_CONTROLS_CONF", qtquickcontrols2File.toLatin1());
}
qDebug() << "Initializing the qmlEngine";
m_qmlEngine.reset(new QQmlEngine);
m_qmlEngine->clearComponentCache();
......@@ -396,11 +390,10 @@ bool ProjectManager::isProjectCached(const QJsonObject &projectInfo)
bool ProjectManager::runCachedProject(const QJsonObject &projectInfo)
{
const QString projectId = projectInfo.value("id").toString();
const QString projectName = projectInfo.value("appName").toString();
qDebug() << "Running cached project " << projectId;
const QString projectPath = m_projectCachePath + "/" + projectId;
return runProject(projectPath, projectName);
return runProject(projectPath);
}
bool ProjectManager::cacheDemoProject(const QByteArray &projectData, const QString &projectName)
......@@ -460,62 +453,58 @@ bool ProjectManager::runDemoProject(const QString &projectName)
{
const QString demoProjectPath = m_demoProjectCachePath + "/" + projectName;
qDebug() << "Running demo project " << projectName << " from " << demoProjectPath;
return runProject(demoProjectPath, projectName);
return runProject(demoProjectPath);
}
void ProjectManager::orientateWindow(Qt::ScreenOrientation orientation)
{
QQuickItem *contentItem = m_quickWindow->contentItem();
QQuickItem *childItem{contentItem->childItems().at(0)};
QQuickItem *targetItem = childItem;
const QRect screenGeometry = QGuiApplication::primaryScreen()->geometry();
const QString orientationString = (orientation == Qt::LandscapeOrientation ? "landscape"
: "portrait");
qDebug() << "Adapting orientation to " << orientationString << " mode";
qDebug() << "Initial sizing:";
qDebug() << "-- Screen size: " << QSTRN(screenGeometry.height()) << " x "
<< QSTRN(screenGeometry.width());
qDebug() << "-- Quick window size: " << QSTRN(m_quickWindow->height()) << " x "
<< QSTRN(m_quickWindow->width());
qDebug() << "-- Child size: " << QSTRN(targetItem->height()) << " x "
<< QSTRN(targetItem->width());
qDebug() << "-- Child pos: " << QSTRN(targetItem->x()) << ", " << QSTRN(targetItem->y());
qDebug() << "-- Child scale: " << QSTRN(targetItem->scale());
qDebug("Calculating the new size and scale...");
const QSizeF newContentSize = targetItem->size().scaled(screenGeometry.size().toSizeF(),
Qt::AspectRatioMode::KeepAspectRatio);
const qreal newScale = newContentSize.height() / targetItem->size().height();
const qreal newX = (targetItem->width() - screenGeometry.width()) / -2.0f;
const qreal newY = (targetItem->height() - screenGeometry.height()) / -2.0f;
targetItem->setScale(newScale);
targetItem->setPosition(QPointF(newX, newY));
qDebug() << "-- Calculated item height: " << QSTRN(newContentSize.height());
qDebug() << "-- Calculated item width: " << QSTRN(newContentSize.width());
qDebug() << "-- Calculated item scale: " << QSTRN(newScale);
qDebug() << "-- Calculated item pos..: " << QSTRN(newX) << "," << QSTRN(newY);
qDebug() << "Final Sizing:";
qDebug() << "-- Child height: " << QSTRN(targetItem->height());
qDebug() << "-- Child width: " << QSTRN(targetItem->width());
qDebug() << "-- Child scale: " << QSTRN(targetItem->scale());
qDebug() << "-- Child pos-x: " << QSTRN(targetItem->x());
qDebug() << "-- Child pos-y: " << QSTRN(targetItem->y());
qDebug() << "Adapting orientation to " << orientationString << " mode"
<< ". Initial sizing:" << Qt::endl
<< "-- Screen size: " << screenGeometry.height() << " x " << screenGeometry.width()
<< Qt::endl
<< "-- Quick window size: " << m_quickWindow->height() << " x "
<< m_quickWindow->width() << Qt::endl
<< "-- Child size: " << childItem->height() << " x " << childItem->width() << Qt::endl
<< "-- Child pos: " << childItem->x() << ", " << childItem->y() << Qt::endl
<< "-- Child scale: " << childItem->scale();
const QSizeF newContentSize = childItem->size().scaled(screenGeometry.size().toSizeF(),
Qt::AspectRatioMode::KeepAspectRatio);
const qreal newScale = newContentSize.height() / childItem->size().height();
const qreal newX = (childItem->width() - screenGeometry.width()) / -2.0f;
const qreal newY = (childItem->height() - screenGeometry.height()) / -2.0f;
childItem->setScale(newScale);
childItem->setPosition(QPointF(newX, newY));
qDebug() << "Calculated props: height-width-scale-posX-posY" << Qt::endl
<< "--" << newContentSize.height() << "-" << newContentSize.width() << "-" << newScale
<< "-" << newX << "-" << newY << Qt::endl
<< "Final Sizing: height-width-scale-posX-posY" << Qt::endl
<< "--" << childItem->height() << "-" << childItem->width() << "-"
<< childItem->scale() << "-" << childItem->x() << "-" << childItem->y();
}
void ProjectManager::showAppWindow()
{
QScreen *screen = QGuiApplication::primaryScreen();
QObject::connect(screen, &QScreen::orientationChanged, this, &ProjectManager::orientateWindow);
connect(screen, &QScreen::orientationChanged, this, &ProjectManager::orientateWindow);
orientateWindow(screen->orientation());
qDebug("Initializing and showing the QML app window");
m_quickWindow->show();
connect(m_quickWindow.data(), &QQuickWindow::closing, this, [this]() {
qDebug() << "QML app window is closing";
m_qmlEngine.reset();
m_qmlComponent.reset();
});
m_quickWindow->showMaximized();
}
void ProjectManager::hideAppWindow()
......
......@@ -40,7 +40,7 @@ public:
explicit ProjectManager(QObject *parent = nullptr);
QString unpackProject(const QByteArray &project, bool extractZip = false);
bool runProject(const QString &projectPath, const QString &projectName);
bool runProject(const QString &projectPath);
bool cacheProject(const QByteArray &projectData, const QJsonObject &projectInfo);
bool isProjectCached(const QJsonObject &projectInfo);
......@@ -71,6 +71,10 @@ private:
// Member functions
QString findFile(const QString &dir, const QString &filter);
QString readQmlProjectFile(const QString &qmlProjectFilePath);
QString getMainQmlFile(const QString &projectPath, const QString &qmlProjectFileContent);
QStringList getImportPaths(const QString &projectPath, const QString &qmlProjectFileContent);
bool isQt6Project(const QString &qmlProjectFileContent);
void parseQmlProjectFile(const QString &fileName, QString *mainFile, QStringList *importPaths);
void cacheProject(const QString &projectName, const QString &projectPath);
......
......@@ -114,4 +114,6 @@ Project {
supportedLanguages: ["en"]
primaryLanguage: "en"
mainUiFile: "main.qml"
}
import QtQuick
import QtQuick.Controls 6.4
import QtQuick.Layouts
Item {
id: header
ColumnLayout {
anchors.fill: parent
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Item {
id: item2
width: 200
height: 200
Layout.preferredHeight: 10
Layout.fillWidth: true
}
Label {
id: infoHeader
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
horizontalAlignment: "AlignHCenter"
text: qsTr("Design Studio Connection Menu")
}
Label {
id: statusLabel
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
horizontalAlignment: "AlignHCenter"
text: "Click the button below to start\nadvertising this device on the network"
Connections {
target: backend
function onNetworkUpdated(newStatus){
statusLabel.text = newStatus
}
}
}
Item {
id: item1
Layout.fillHeight: true
Layout.fillWidth: true
}
Button {
property bool isRunning: false
id: startConnection
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Layout.fillWidth: true
height: 100
text: qsTr("Start Connection")
onClicked: {
if (isRunning) {
backend.disconnectDesignStudio()
isRunning = false
text = qsTr("Start Connection")
statusLabel.text = "Click the button below to start\nadvertising this device on the network"
} else {
let retval = backend.connectDesignStudio()
if (retval) {
text = qsTr("Stop Connection")
isRunning = true
}
}
}
}
}
}
......@@ -105,6 +105,12 @@ Rectangle {
id: logsPage
Layout.fillWidth: true
}
Network {
id: networkPage
Layout.fillWidth: true
}
AboutHeader {
id: headerPage
Layout.fillWidth: true
......@@ -177,7 +183,6 @@ Rectangle {
Layout.fillWidth: true
checkable: true
autoExclusive: true
Connections {
target: logs
function onClicked(){
......@@ -186,6 +191,22 @@ Rectangle {
}
}
TabButton {
id: network
text: qsTr("Network")
Layout.fillWidth: true
checkable: true
autoExclusive: true
visible: false;
Connections {
target: network
function onClicked(){
stackLayout.currentIndex = 3
}
}
}
TabButton {
id: about
......@@ -197,7 +218,7 @@ Rectangle {
Connections {
target: about
function onClicked(){
stackLayout.currentIndex = 3
stackLayout.currentIndex = 4
}
}
}
......
......@@ -4,6 +4,7 @@
<file>main.qml</file>
<file>HomePage.qml</file>
<file>Logs.qml</file>
<file>Network.qml</file>
<file>ExamplesPage.qml</file>
<file>AboutHeader.qml</file>
</qresource>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment