From 165f46660a422e65372856da6200fbc36e76151d Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 30 Nov 2017 14:59:14 +0200 Subject: Remove widgets from viewer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also includes other improvements to viewer application. Task-number: QT3DS-75 Task-number: QT3DS-687 Task-number: QT3DS-689 Change-Id: I6a5054b4b821d652f536f2c7eaa1bb4c508250c3 Reviewed-by: Tomi Korpipää Reviewed-by: Janne Koskinen Reviewed-by: Antti Määttä Reviewed-by: Miikka Heikkinen --- doc/src/04-viewer/2-commandline.qdoc | 14 + src/Viewer/Qt3DViewer/Qt3DViewer.pro | 11 +- src/Viewer/Qt3DViewer/Viewer.qrc | 18 +- src/Viewer/Qt3DViewer/main.cpp | 157 ++++- src/Viewer/Qt3DViewer/mainwindow.cpp | 670 --------------------- src/Viewer/Qt3DViewer/mainwindow.h | 115 ---- src/Viewer/Qt3DViewer/mainwindow.ui | 221 ------- src/Viewer/Qt3DViewer/qml/StyledButton.qml | 52 ++ src/Viewer/Qt3DViewer/qml/StyledMenu.qml | 95 +++ src/Viewer/Qt3DViewer/qml/StyledMenuButton.qml | 71 +++ src/Viewer/Qt3DViewer/qml/StyledMenuItem.qml | 159 +++++ src/Viewer/Qt3DViewer/qml/StyledMenuSeparator.qml | 47 ++ src/Viewer/Qt3DViewer/qml/main.qml | 542 +++++++++++++++++ src/Viewer/Qt3DViewer/remotedeploymentreceiver.cpp | 40 +- src/Viewer/Qt3DViewer/remotedeploymentreceiver.h | 8 +- .../Qt3DViewer/resources/images/ViewerDoc.ico | Bin 370070 -> 0 bytes src/Viewer/Qt3DViewer/resources/images/arrow.png | Bin 0 -> 523 bytes .../Qt3DViewer/resources/images/arrow@2x.png | Bin 0 -> 600 bytes src/Viewer/Qt3DViewer/resources/images/check.png | Bin 0 -> 502 bytes .../Qt3DViewer/resources/images/check@2x.png | Bin 0 -> 638 bytes .../Qt3DViewer/resources/images/icon_256x256.png | Bin 10229 -> 0 bytes .../resources/images/icon_256x256@2x.png | Bin 22963 -> 0 bytes .../Qt3DViewer/resources/wordlist/wordlist.txt | 81 --- src/Viewer/Qt3DViewer/style_android.qss | 130 ---- src/Viewer/Qt3DViewer/viewer.cpp | 359 +++++++++++ src/Viewer/Qt3DViewer/viewer.h | 120 ++++ src/Viewer/Qt3DViewer/viewer.qml | 39 -- 27 files changed, 1617 insertions(+), 1332 deletions(-) delete mode 100644 src/Viewer/Qt3DViewer/mainwindow.cpp delete mode 100644 src/Viewer/Qt3DViewer/mainwindow.h delete mode 100644 src/Viewer/Qt3DViewer/mainwindow.ui create mode 100644 src/Viewer/Qt3DViewer/qml/StyledButton.qml create mode 100644 src/Viewer/Qt3DViewer/qml/StyledMenu.qml create mode 100644 src/Viewer/Qt3DViewer/qml/StyledMenuButton.qml create mode 100644 src/Viewer/Qt3DViewer/qml/StyledMenuItem.qml create mode 100644 src/Viewer/Qt3DViewer/qml/StyledMenuSeparator.qml create mode 100644 src/Viewer/Qt3DViewer/qml/main.qml delete mode 100644 src/Viewer/Qt3DViewer/resources/images/ViewerDoc.ico create mode 100644 src/Viewer/Qt3DViewer/resources/images/arrow.png create mode 100644 src/Viewer/Qt3DViewer/resources/images/arrow@2x.png create mode 100644 src/Viewer/Qt3DViewer/resources/images/check.png create mode 100644 src/Viewer/Qt3DViewer/resources/images/check@2x.png delete mode 100644 src/Viewer/Qt3DViewer/resources/images/icon_256x256.png delete mode 100644 src/Viewer/Qt3DViewer/resources/images/icon_256x256@2x.png delete mode 100644 src/Viewer/Qt3DViewer/resources/wordlist/wordlist.txt delete mode 100644 src/Viewer/Qt3DViewer/style_android.qss create mode 100644 src/Viewer/Qt3DViewer/viewer.cpp create mode 100644 src/Viewer/Qt3DViewer/viewer.h delete mode 100644 src/Viewer/Qt3DViewer/viewer.qml diff --git a/doc/src/04-viewer/2-commandline.qdoc b/doc/src/04-viewer/2-commandline.qdoc index d3ab65bb..d9b4985c 100644 --- a/doc/src/04-viewer/2-commandline.qdoc +++ b/doc/src/04-viewer/2-commandline.qdoc @@ -80,6 +80,20 @@ The following command line options are supported: \li --seq-outfile \li Output file name base for the image sequence. The default value is derived from the presentation file name. + \row + \li --connect + \li If this parameter is specified, the viewer is started in connection mode. + The default value is 36000. + \row + \li --fullscreen + \li Starts the viewer in fullscreen mode. + \row + \li --maximized + \li Starts the viewer in maximized mode. + \row + \li --windowgeometry + \li Specifies the initial window geometry using the X11-syntax. + For example: \c{1000x800+50+50} \endtable Example - Creating a one minute 4k image sequence of a presentation: diff --git a/src/Viewer/Qt3DViewer/Qt3DViewer.pro b/src/Viewer/Qt3DViewer/Qt3DViewer.pro index a7f95931..1fe9a59b 100644 --- a/src/Viewer/Qt3DViewer/Qt3DViewer.pro +++ b/src/Viewer/Qt3DViewer/Qt3DViewer.pro @@ -2,31 +2,28 @@ include($$PWD/../../Runtime/commoninclude.pri) TEMPLATE = app TARGET = Qt3DViewer -QT += widgets opengl qml quickwidgets quickcontrols2 studio3d-private +QT += qml quickcontrols2 studio3d-private INCLUDEPATH += $$PWD/../qmlviewer -FORMS += mainwindow.ui RESOURCES += Viewer.qrc -RC_FILE += Viewer.rc +RC_ICONS = resources/images/3D-studio-viewer.ico ICON = resources/images/viewer.icns SOURCES += \ - $$PWD/../qmlviewer/Qt3DSViewPlugin.cpp \ $$PWD/../qmlviewer/Qt3DSView.cpp \ $$PWD/../qmlviewer/Qt3DSRenderer.cpp \ $$PWD/../qmlviewer/q3dspresentationitem.cpp \ main.cpp \ - mainwindow.cpp \ + viewer.cpp \ remotedeploymentreceiver.cpp HEADERS += \ - $$PWD/../qmlviewer/Qt3DSViewPlugin.h \ $$PWD/../qmlviewer/Qt3DSView.h \ $$PWD/../qmlviewer/Qt3DSRenderer.h \ $$PWD/../qmlviewer/q3dspresentationitem.h \ - mainwindow.h \ + viewer.h \ remotedeploymentreceiver.h LIBS += \ diff --git a/src/Viewer/Qt3DViewer/Viewer.qrc b/src/Viewer/Qt3DViewer/Viewer.qrc index 72650fb2..c808dca5 100644 --- a/src/Viewer/Qt3DViewer/Viewer.qrc +++ b/src/Viewer/Qt3DViewer/Viewer.qrc @@ -1,12 +1,14 @@ - resources/images/icon_256x256.png - resources/images/icon_256x256@2x.png - viewer.qml - ../../Authoring/Studio/style.qss - ../../Authoring/Studio/images/arrow_down.png - ../../Authoring/Studio/images/arrow_up.png - ../../Authoring/Studio/images/separator.png - style_android.qss + resources/images/arrow.png + resources/images/arrow@2x.png + resources/images/check.png + resources/images/check@2x.png + qml/main.qml + qml/StyledMenu.qml + qml/StyledMenuButton.qml + qml/StyledMenuItem.qml + qml/StyledMenuSeparator.qml + qml/StyledButton.qml diff --git a/src/Viewer/Qt3DViewer/main.cpp b/src/Viewer/Qt3DViewer/main.cpp index d8665545..8469a350 100644 --- a/src/Viewer/Qt3DViewer/main.cpp +++ b/src/Viewer/Qt3DViewer/main.cpp @@ -28,19 +28,22 @@ ** ****************************************************************************/ -#include "mainwindow.h" +#include "viewer.h" -#include +#include +#include +#include #include #include +#include #include #include +#include +#include +#include int main(int argc, char *argv[]) { - // To enable QOpenGLWidget to work on macOS, we must set the default - // QSurfaceFormat before QApplication is created. Otherwise context-sharing - // fails and QOpenGLWidget breaks. #if defined(Q_OS_MACOS) QSurfaceFormat openGLFormat; openGLFormat.setRenderableType(QSurfaceFormat::OpenGL); @@ -56,13 +59,14 @@ int main(int argc, char *argv[]) QCoreApplication::setOrganizationDomain("qt.io"); QCoreApplication::setApplicationName("Qt 3D Viewer"); - QApplication a(argc, argv); + QGuiApplication a(argc, argv); QCommandLineParser parser; parser.addHelpOption(); - parser.addPositionalArgument("file", - QCoreApplication::translate("main", "The presentation file to open."), - QCoreApplication::translate("main", "[file]")); + parser.addPositionalArgument( + "file", + QCoreApplication::translate("main", "The presentation file to open."), + QCoreApplication::translate("main", "[file]")); parser.addOption({"sequence", QCoreApplication::translate("main", @@ -87,8 +91,8 @@ int main(int argc, char *argv[]) QCoreApplication::translate("main", "fps"), QString::number(60)}); parser.addOption({"seq-interval", QCoreApplication::translate("main", - "Time interval between frames in the sequence\n" - "in milliseconds. The seq-fps argument is ignored\n" + "Time interval between frames in\n" + "the sequence in milliseconds. The seq-fps argument is ignored" "if this argument is used."), QCoreApplication::translate("main", "ms"), QString::number(0)}); parser.addOption({"seq-width", @@ -111,6 +115,24 @@ int main(int argc, char *argv[]) "Output filename base for the image sequence.\n" "The default value is derived from the presentation filename."), QCoreApplication::translate("main", "file"), QStringLiteral("")}); + parser.addOption({"connect", + QCoreApplication::translate("main", + "If this parameter is specified, the viewer\n" + "is started in connection mode.\n" + "The default value is 36000."), + QCoreApplication::translate("main", "port"), QString::number(36000)}); + parser.addOption({"fullscreen", + QCoreApplication::translate("main", + "Starts the viewer in fullscreen mode.\n")}); + parser.addOption({"maximized", + QCoreApplication::translate("main", + "Starts the viewer in maximized mode.")}); + parser.addOption({"windowgeometry", + QCoreApplication::translate("main", + "Specifies the initial\n" + "window geometry using the X11-syntax.\n" + "For example: 1000x800+50+50"), + QCoreApplication::translate("main", "geometry"), QStringLiteral("")}); parser.process(a); const QStringList files = parser.positionalArguments(); @@ -127,7 +149,83 @@ int main(int argc, char *argv[]) Q3DSImageSequenceGenerator *generator = nullptr; - MainWindow w(generateSequence); + Viewer viewer(generateSequence); + + // Figure out control size multiplier for devices using touch screens to ensure all controls + // have minimum usable size. + qreal sizeMultiplier = 1.0; + const auto touchDevices = QTouchDevice::devices(); + if (touchDevices.size() > 0) { + // Find out the actual screen logical pixel size. Typically touch devices we care about + // only have a single screen, so we just check primary screen. + const auto screens = QGuiApplication::screens(); + if (screens.size() > 0) { + QScreen *screen = screens.at(0); + qreal dpi = screen->physicalDotsPerInch() / screen->devicePixelRatio(); + sizeMultiplier = dpi / 40.0; // divider chosen empirically + } + } + + QQmlApplicationEngine engine; + // Set import paths so that standalone installation works + QString extraImportPath1(QStringLiteral("%1/qml")); +#ifdef Q_OS_MACOS + QString extraImportPath2(QStringLiteral("%1/../../../../qml")); +#else + QString extraImportPath2(QStringLiteral("%1/../qml")); +#endif + engine.addImportPath(extraImportPath1.arg(QGuiApplication::applicationDirPath())); + engine.addImportPath(extraImportPath2.arg(QGuiApplication::applicationDirPath())); + + QQmlContext *ctx = engine.rootContext(); + ctx->setContextProperty(QStringLiteral("_menuBackgroundColor"), QColor("#404244")); + ctx->setContextProperty(QStringLiteral("_menuSelectionColor"), QColor("#46a2da")); + ctx->setContextProperty(QStringLiteral("_menuBorderColor"), QColor("#727476")); + ctx->setContextProperty(QStringLiteral("_dialogBorderColor"), QColor("#404244")); + ctx->setContextProperty(QStringLiteral("_dialogBackgroundColor"), QColor("#2e2f30")); + ctx->setContextProperty(QStringLiteral("_dialogFieldColor"), QColor("#404244")); + ctx->setContextProperty(QStringLiteral("_dialogFieldBorderColor"), QColor("#262829")); + ctx->setContextProperty(QStringLiteral("_textColor"), QColor("#ffffff")); + ctx->setContextProperty(QStringLiteral("_disabledColor"), QColor("#727476")); + ctx->setContextProperty(QStringLiteral("_fontSize"), int(12 * sizeMultiplier)); + ctx->setContextProperty(QStringLiteral("_controlBaseHeight"), int(24 * sizeMultiplier)); + ctx->setContextProperty(QStringLiteral("_controlBaseWidth"), int(80 * sizeMultiplier)); + ctx->setContextProperty(QStringLiteral("_controlPadding"), int(12 * sizeMultiplier)); + ctx->setContextProperty(QStringLiteral("_viewerHelper"), &viewer); + qmlRegisterUncreatableType( + "Qt3DStudioViewer", 1, 0, "ViewerHelper", + QCoreApplication::translate("main", + "Creation of ViewerHelper not allowed from QML")); + engine.load(QUrl(QLatin1String("qrc:/qml/main.qml"))); + Q_ASSERT(engine.rootObjects().size() > 0); + QWindow *appWindow = qobject_cast(engine.rootObjects().at(0)); + Q_ASSERT(appWindow); + viewer.setQmlRootObject(appWindow); + + if (parser.isSet(QStringLiteral("windowgeometry"))) { + int width = 1280; + int height = 768; + int x = 50; + int y = 50; + QString geometryStr = parser.value(QStringLiteral("windowgeometry")); + const QStringList splitPlus = geometryStr.split(QLatin1Char('+')); + if (splitPlus.size() > 0) { + const QStringList splitX = splitPlus[0].split(QLatin1Char('x')); + if (splitX.size() >= 2) { + width = splitX[0].toInt(); + height = splitX[1].toInt(); + } + if (splitPlus.size() >= 3) { + x = splitPlus[1].toInt(); + y = splitPlus[2].toInt(); + } + } + appWindow->setGeometry(x, y, width, height); + } + if (parser.isSet(QStringLiteral("fullscreen"))) + appWindow->setVisibility(QWindow::FullScreen); + else if (parser.isSet(QStringLiteral("maximized"))) + appWindow->setVisibility(QWindow::Maximized); if (generateSequence) { if (files.count() != 1) { @@ -136,10 +234,10 @@ int main(int argc, char *argv[]) } generator = new Q3DSImageSequenceGenerator; QObject::connect(generator, &Q3DSImageSequenceGenerator::progress, - &w, &MainWindow::generatorProgress); + &viewer, &Viewer::generatorProgress); QObject::connect(generator, &Q3DSImageSequenceGenerator::finished, - &w, &MainWindow::generatorFinished); - w.setGeneratorDetails(files.first()); + &viewer, &Viewer::generatorFinished); + viewer.setGeneratorDetails(files.first()); generator->generateImageSequence( files.first(), parser.value("seq-start").toDouble(), @@ -151,21 +249,22 @@ int main(int argc, char *argv[]) parser.value("seq-outpath"), parser.value("seq-outfile")); } else if (!files.isEmpty()) { - w.loadFile(files.first()); + // Load the presentation after window has been exposed to give QtQuick time to construct + // the application window properly + QTimer *exposeTimer = new QTimer(appWindow); + QObject::connect(exposeTimer, &QTimer::timeout, [&](){ + if (appWindow->isExposed()) { + exposeTimer->stop(); + exposeTimer->deleteLater(); + viewer.loadFile(files.first()); + } + }); + exposeTimer->start(0); + } else if (parser.isSet(QStringLiteral("connect"))) { + viewer.setContentView(Viewer::ConnectView); + viewer.setConnectPort(parser.value(QStringLiteral("connect")).toInt()); + viewer.connectRemote(); } - w.show(); -#ifndef Q_OS_ANDROID - QFile styleFile(":/style.qss"); -#else - // We need a different stylesheet for Android, or the controls are too small, and some of - // them aren't displayed correctly (see QTBUG-41773) - QFile styleFile(":/style_android.qss"); -#endif - styleFile.open(QFile::ReadOnly); - - QString style(styleFile.readAll()); - a.setStyleSheet(style); - return a.exec(); } diff --git a/src/Viewer/Qt3DViewer/mainwindow.cpp b/src/Viewer/Qt3DViewer/mainwindow.cpp deleted file mode 100644 index 3c2972b7..00000000 --- a/src/Viewer/Qt3DViewer/mainwindow.cpp +++ /dev/null @@ -1,670 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 - 2016 NVIDIA Corporation. -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt 3D Studio. -** -** $QT_BEGIN_LICENSE:GPL$ -** 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 or (at your option) any later version -** approved by the KDE Free Qt Foundation. The licenses are as published by -** the Free Software Foundation and appearing in the file LICENSE.GPL3 -** 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. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Qt3DSView.h" -#include "q3dspresentationitem.h" - -#include "mainwindow.h" -#include "ui_mainwindow.h" - -MainWindow::MainWindow(bool generatorMode, QWidget *parent) - : QMainWindow(parent) - , ui(new Ui::MainWindow) - , m_studio3D(0) - , m_connectionInfo(0) - , m_remoteDeploymentReceiver(0) - , m_generatorMode(generatorMode) -{ - ui->setupUi(this); - - if (!m_generatorMode) { - resize(1280, 720); - ui->actionOpen->setShortcut(QKeySequence::Open); - QList shortcuts; - shortcuts.push_back(QKeySequence(QKeySequence::Quit)); - shortcuts.push_back(QKeySequence("CTRL+Q")); - ui->actionQuit->setShortcuts(shortcuts); - ui->actionReload->setShortcut(QKeySequence::Refresh); - - QStringList strArg = QApplication::arguments(); - if (strArg.size() >= 2) { - QFileInfo theFilePath(strArg[1]); - if (theFilePath.exists()) { - m_openFileDir = theFilePath.path(); - QSettings().setValue("DirectoryOfLastOpen", m_openFileDir); - } - } - -#ifdef Q_OS_ANDROID - m_openFileDir = QStringLiteral("/sdcard/qt3dviewer"); // Add default folder for Android -#else - // Allow drops. Not usable for Android. - setAcceptDrops(true); -#endif - - addAction(ui->actionFull_Screen); - addAction(ui->actionShowOnScreenStats); - addAction(ui->actionBorder); - addAction(ui->actionToggle_Scale_Mode); - addAction(ui->actionToggle_Shade_Mode); - } else { - ui->menuBar->clear(); - ui->menuBar->addAction(ui->actionQuit); - resize(700, 100); - } - - // Set import paths so that standalone installation works - QString extraImportPath1(QStringLiteral("%1/qml")); -#ifdef Q_OS_MACOS - QString extraImportPath2(QStringLiteral("%1/../../../../qml")); -#else - QString extraImportPath2(QStringLiteral("%1/../qml")); -#endif - ui->quickWidget->engine()->addImportPath( - extraImportPath1.arg(QGuiApplication::applicationDirPath())); - ui->quickWidget->engine()->addImportPath( - extraImportPath2.arg(QGuiApplication::applicationDirPath())); - - ui->quickWidget->setSource(QUrl("qrc:/viewer.qml")); - - if (m_generatorMode) - setupGeneratorUI(); - - updateUI(); -} - -MainWindow::~MainWindow() -{ -} - -void MainWindow::on_actionToggle_Scale_Mode_triggered() -{ - switch (viewer()->viewerSettings()->scaleMode()) - { - case Q3DSViewerSettings::ScaleModeCenter: - viewer()->viewerSettings()->setScaleMode(Q3DSViewerSettings::ScaleModeFill); - break; - case Q3DSViewerSettings::ScaleModeFill: - viewer()->viewerSettings()->setScaleMode(Q3DSViewerSettings::ScaleModeFit); - break; - case Q3DSViewerSettings::ScaleModeFit: - viewer()->viewerSettings()->setScaleMode(Q3DSViewerSettings::ScaleModeCenter); - break; - } - updateUI(); -} - -void MainWindow::on_actionToggle_Shade_Mode_triggered() -{ - switch (viewer()->viewerSettings()->shadeMode()) - { - case Q3DSViewerSettings::ShadeModeShaded: - viewer()->viewerSettings()->setShadeMode(Q3DSViewerSettings::ShadeModeShadedWireframe); - break; - case Q3DSViewerSettings::ShadeModeShadedWireframe: - viewer()->viewerSettings()->setShadeMode(Q3DSViewerSettings::ShadeModeShaded); - break; - } - updateUI(); -} - -void MainWindow::on_actionBorder_triggered() -{ - Q3DSView *view = viewer(); - if (!view) - return; - - QColor matte = view->viewerSettings()->matteColor(); - if (matte == QColor(Qt::black) || !matte.isValid()) - view->viewerSettings()->setMatteColor(QColor(50, 50, 50)); - else - view->viewerSettings()->setMatteColor(Qt::black); - - updateUI(); -} - -void MainWindow::on_actionFull_Screen_triggered() -{ - if (ui->actionFull_Screen->isChecked()) { - showFullScreen(); - ui->menuBar->hide(); - } else { - showNormal(); - ui->menuBar->show(); - } -} - -void MainWindow::on_actionShowOnScreenStats_triggered() -{ - Q3DSView *view = viewer(); - if (!view) - return; - - view->viewerSettings()->setShowRenderStats(!view->viewerSettings()->isShowRenderStats()); - updateUI(); -} - -void MainWindow::on_actionQuit_triggered() -{ - delete m_studio3D; - m_studio3D = 0; - close(); -} - -void MainWindow::on_actionOpen_triggered() -{ - QSettings settings; - if (m_openFileDir.size() == 0) { - m_openFileDir - = settings.value("DirectoryOfLastOpen", QString("")).toString(); - } - - QString filename = QFileDialog::getOpenFileName( - this, tr("Open File or Project"), m_openFileDir, - tr("All supported formats (*.uip *.uia *.uiab);;Studio UI Presentation " - "(*.uip);;Application file (*.uia);;Binary Application (*.uiab)"), - NULL, QFileDialog::DontUseNativeDialog); - - if (filename.size() == 0) - return; - - QFileInfo theInfo(filename); - m_openFileDir = theInfo.path(); - settings.setValue("DirectoryOfLastOpen", m_openFileDir); - - loadFile(filename); -} - -void MainWindow::on_actionConnect_triggered() -{ - if (m_remoteDeploymentReceiver) { - delete m_remoteDeploymentReceiver; - m_remoteDeploymentReceiver = 0; - delete m_connectionInfo; - m_connectionInfo = 0; - if (m_studio3D) - m_studio3D->setVisible(true); - - updateUI(); - return; - } - - m_remoteDeploymentReceiver = new RemoteDeploymentReceiver(this); - if (m_remoteDeploymentReceiver->canceled()) { - delete m_remoteDeploymentReceiver; - m_remoteDeploymentReceiver = 0; - ui->actionConnect->setChecked(false); - return; - } - - if (!m_remoteDeploymentReceiver->startServer()) { - QString msg = "Unable to connect to remote project.\n"; - QMessageBox::warning(this, "Connection Error", msg, QMessageBox::Close); - delete m_remoteDeploymentReceiver; - m_remoteDeploymentReceiver = 0; - updateUI(); - return; - } - - int port = m_remoteDeploymentReceiver->serverPort(); - QString message; - QTextStream stream(&message); - stream << "Connection Info\n" - << "Address: " << m_remoteDeploymentReceiver->hostAddress().toString() << "\n" - << "Port: " + QString::number(port); - - QQmlEngine *engine = ui->quickWidget->engine(); - QQuickItem *root = ui->quickWidget->rootObject(); - - QByteArray qml = "import QtQuick 2.7\n" - "import QtQuick.Controls 2.2\n" - "Label {\n" - " color: \"White\"\n" - " horizontalAlignment: Text.AlignHCenter\n" - " verticalAlignment: Text.AlignVCenter\n" - " anchors.fill: parent\n" - " font.pixelSize: 42\n" - "}"; - - QQmlComponent component(engine); - component.setData(qml, QUrl()); - - if (component.isError()) { - qCritical() << "error" << component.errors(); - return; - } - - m_connectionInfo = qobject_cast(component.create()); - m_connectionInfo->setProperty("text", message); - - QQmlEngine::setObjectOwnership(m_connectionInfo, QQmlEngine::CppOwnership); - m_connectionInfo->setParentItem(root); - m_connectionInfo->setParent(engine); - - connect(m_remoteDeploymentReceiver, &RemoteDeploymentReceiver::remoteConnected, - this, &MainWindow::remoteConnected); - - connect(m_remoteDeploymentReceiver, &RemoteDeploymentReceiver::remoteDisconnected, - this, &MainWindow::remoteDisconnected); - - connect(m_remoteDeploymentReceiver, &RemoteDeploymentReceiver::projectChanging, - this, &MainWindow::remoteProjectChanging); - - connect(m_remoteDeploymentReceiver, &RemoteDeploymentReceiver::projectChanged, - this, &MainWindow::loadRemoteDeploymentReceiver); - - updateUI(); -} - -void MainWindow::on_actionReload_triggered() -{ - if (Q3DSView *view = viewer()) { - view->reset(); - updateUI(); - } -} - -Q3DSView *MainWindow::viewer() const -{ - return m_studio3D; -} - -void MainWindow::loadFile(const QString &filename) -{ - QFileInfo fileInfo(filename); - if (!fileInfo.exists()) - return; - - QUrl sourceUrl = QUrl::fromLocalFile(fileInfo.absoluteFilePath()); - - if (m_studio3D) { - viewer()->presentation()->setSource(sourceUrl); - viewer()->reset(); - return; - } - delete m_errorInfo; - m_errorInfo = nullptr; - - QQmlEngine *engine = ui->quickWidget->engine(); - QQuickItem *root = ui->quickWidget->rootObject(); - - QByteArray qml = "import QtStudio3D 1.0\n" - "Studio3D {\n" - " id: studio3D\n" - " anchors.fill: parent\n" - " focus: true\n" - "}"; - - QQmlComponent component(engine); - component.setData(qml, QUrl()); - - if (component.isError()) { - qDebug() << "error" << component.errors(); - return; - } - - m_studio3D = static_cast(component.create()); - connect(m_studio3D, &Q3DSView::errorChanged, this, &MainWindow::onErrorChanged, - Qt::QueuedConnection); - viewer()->presentation()->setSource(sourceUrl); - - QQmlEngine::setObjectOwnership(m_studio3D, QQmlEngine::CppOwnership); - m_studio3D->setParentItem(root); - m_studio3D->setParent(engine); - -#ifdef Q_OS_ANDROID - // We have custom mouse event handling in android - viewer()->setIgnoreEvents(true, false, false); -#endif - - updateUI(); -} - -void MainWindow::on_actionCenter_triggered() -{ - if (Q3DSView *view = viewer()) { - view->viewerSettings()->setScaleMode(Q3DSViewerSettings::ScaleModeCenter); - updateUI(); - } -} - -void MainWindow::on_actionScale_To_Fit_triggered() -{ - if (Q3DSView *view = viewer()) { - view->viewerSettings()->setScaleMode(Q3DSViewerSettings::ScaleModeFit); - updateUI(); - } -} - -void MainWindow::on_actionScale_To_Fill_triggered() -{ - if (Q3DSView *view = viewer()) { - view->viewerSettings()->setScaleMode(Q3DSViewerSettings::ScaleModeFill); - updateUI(); - } -} - -QString MainWindow::convertMimeDataToFilename(const QMimeData *mimeData) -{ - if (mimeData->hasUrls()) { - for (const QUrl &url : mimeData->urls()) { - QString str = url.toLocalFile(); - if (str.isEmpty() == false) { - if ((QFileInfo(str).suffix() == "uip") - || (QFileInfo(str).suffix() == "uia") - || (QFileInfo(str).suffix() == "uiab")) - { - return str; - } - } - } - } - return QString(); -} - -void MainWindow::setupGeneratorUI() -{ - QQmlEngine *engine = ui->quickWidget->engine(); - QQuickItem *root = ui->quickWidget->rootObject(); - - QByteArray qml = "import QtQuick 2.7\n" - "import QtQuick.Controls 2.2\n" - "Item {\n" - " property alias mainText: mainLabel.text\n" - " property alias detailsText: detailsLabel.text\n" - " anchors.fill: parent\n" - " Label {\n" - " id: mainLabel\n" - " color: \"White\"\n" - " horizontalAlignment: Text.AlignHCenter\n" - " verticalAlignment: Text.AlignVCenter\n" - " anchors.top: parent.top\n" - " anchors.left: parent.left\n" - " anchors.right: parent.right\n" - " height: parent.height / 2\n" - " font.pixelSize: width / 40\n" - " text: \"Image sequence generation initializing...\"\n" - " }\n" - " Label {\n" - " id: detailsLabel\n" - " color: \"White\"\n" - " horizontalAlignment: Text.AlignHCenter\n" - " verticalAlignment: Text.AlignTop\n" - " anchors.top: mainLabel.bottom\n" - " anchors.left: parent.left\n" - " anchors.right: parent.right\n" - " height: parent.height / 2\n" - " font.pixelSize: width / 50\n" - " }\n" - "}"; - - QQmlComponent component(engine); - component.setData(qml, QUrl()); - - if (component.isError()) { - qCritical() << "Error setting up generator UI:" << component.errors(); - return; - } - - m_generatorInfo = qobject_cast(component.create()); - - QQmlEngine::setObjectOwnership(m_generatorInfo, QQmlEngine::CppOwnership); - m_generatorInfo->setParentItem(root); - m_generatorInfo->setParent(engine); -} - -void MainWindow::dragEnterEvent(QDragEnterEvent *event) -{ - if (convertMimeDataToFilename(event->mimeData()).isEmpty() == false) - event->acceptProposedAction(); -} - -void MainWindow::dropEvent(QDropEvent *event) -{ - QString fileName = convertMimeDataToFilename(event->mimeData()); - if (fileName.isEmpty() == false) - loadFile(fileName); -} - -void MainWindow::showEvent(QShowEvent *event) -{ - QMainWindow::showEvent(event); - if (!m_generatorMode) { - QSettings settings(QCoreApplication::organizationName(), - QCoreApplication::applicationName()); - restoreGeometry(settings.value("mainWindowGeometry").toByteArray()); - restoreState(settings.value("mainWindowState").toByteArray()); - } - updateUI(); -} - -void MainWindow::closeEvent(QCloseEvent *event) -{ - if (!m_generatorMode) { - QSettings settings(QCoreApplication::organizationName(), - QCoreApplication::applicationName()); - settings.setValue("mainWindowGeometry", saveGeometry()); - settings.setValue("mainWindowState", saveState()); - } - QMainWindow::closeEvent(event); -} - -#ifdef Q_OS_ANDROID -void MainWindow::mousePressEvent(QMouseEvent *event) -{ - m_swipeStart = event->pos(); - - Q3DSView *view = viewer(); - if (view) - view->presentation()->mousePressEvent(event); - - event->accept(); -} - -void MainWindow::mouseReleaseEvent(QMouseEvent *event) -{ - Q3DSView *view = viewer(); - if (view) - view->presentation()->mouseReleaseEvent(event); - - event->accept(); -} - -void MainWindow::mouseMoveEvent(QMouseEvent *event) -{ - // Fake swipe down event, as SwipeGesture doesn't work (unless you use 3 fingers..) - int swipeLength = height() / 10; - if (ui->actionFull_Screen->isChecked() && event->pos().y() > m_swipeStart.y() + swipeLength) { - ui->actionFull_Screen->setChecked(false); - on_actionFull_Screen_triggered(); - } - - Q3DSView *view = viewer(); - if (view) - view->presentation()->mouseMoveEvent(event); - - event->accept(); -} -#endif - -void MainWindow::updateUI() -{ - ui->actionConnect->setChecked(m_remoteDeploymentReceiver); - - bool displayConnection = m_remoteDeploymentReceiver - && !m_remoteDeploymentReceiver->isProjectDeployed(); - - if (m_connectionInfo) - m_connectionInfo->setVisible(displayConnection); - - if (m_studio3D) - m_studio3D->setVisible(!displayConnection); - - Q3DSView *view = viewer(); - if (!view) - return; - - Q3DSViewerSettings::ScaleMode scaleMode = view->viewerSettings()->scaleMode(); - ui->actionCenter->setChecked(scaleMode == Q3DSViewerSettings::ScaleModeCenter); - ui->actionScale_To_Fit->setChecked(scaleMode == Q3DSViewerSettings::ScaleModeFit); - ui->actionScale_To_Fill->setChecked(scaleMode == Q3DSViewerSettings::ScaleModeFill); - - QColor matte = view->viewerSettings()->matteColor(); - ui->actionBorder->setChecked(matte.isValid() && matte != QColor(Qt::black)); - ui->actionShowOnScreenStats->setChecked(view->viewerSettings()->isShowRenderStats()); -} - -void MainWindow::loadRemoteDeploymentReceiver() -{ - Q_ASSERT(m_remoteDeploymentReceiver); - const QString remote = m_remoteDeploymentReceiver->fileName(); - loadFile(remote); - updateUI(); -} - -void MainWindow::remoteProjectChanging() -{ - updateUI(); -} - -void MainWindow::remoteConnected() -{ - m_connectionInfo->setProperty("text", "Remote Connected"); - updateUI(); -} - -void MainWindow::remoteDisconnected() -{ - m_connectionInfo->setProperty("text", "Remote Disconnected"); - updateUI(); -} - -void MainWindow::onErrorChanged(const QString &error) -{ - if (error.isEmpty()) { - delete m_errorInfo; - m_errorInfo = nullptr; - } else { - if (!m_errorInfo) { - QQmlEngine *engine = ui->quickWidget->engine(); - QQuickItem *root = ui->quickWidget->rootObject(); - - QByteArray qml = "import QtQuick 2.7\n" - "import QtQuick.Controls 2.2\n" - "Label {\n" - " color: \"White\"\n" - " horizontalAlignment: Text.AlignHCenter\n" - " verticalAlignment: Text.AlignVCenter\n" - " anchors.fill: parent\n" - " font.pixelSize: width / 80\n" - "}"; - - QQmlComponent component(engine); - component.setData(qml, QUrl()); - - if (component.isError()) { - qCritical() << "Error setting up error UI:" << component.errors(); - return; - } - - m_errorInfo = qobject_cast(component.create()); - m_errorInfo->setParentItem(root); - m_errorInfo->setParent(engine); - QQmlEngine::setObjectOwnership(m_errorInfo, QQmlEngine::CppOwnership); - } - m_errorInfo->setProperty("text", m_studio3D->error()); - - delete m_studio3D; - m_studio3D = nullptr; - } -} - -void MainWindow::generatorProgress(int totalFrames, int frameCount) -{ - QString progressString; - if (frameCount >= totalFrames) { - progressString = - QCoreApplication::translate( - "main", "Image sequence generation done! (%2 frames generated)") - .arg(totalFrames); - } else { - progressString = - QCoreApplication::translate("main", "Image sequence generation progress: %1 / %2") - .arg(frameCount).arg(totalFrames); - } - m_generatorInfo->setProperty("mainText", progressString); -} - -void MainWindow::generatorFinished(bool success, const QString &details) -{ - if (success) { - m_generatorInfo->setProperty("detailsText", details); - } else { - QString mainString = - QCoreApplication::translate("main", "Image sequence generation failed:"); - m_generatorInfo->setProperty("mainText", mainString); - m_generatorInfo->setProperty("detailsText", details); - } -} - -void MainWindow::updateProgress(int percent) -{ - QString progress = QStringLiteral("Receiving ("); - progress.append(QString::number(percent)); - progress.append("%)"); - // Don't wait for 100%, as it'll already start loading and text isn't updated anymore - if (percent >= 99) - m_connectionInfo->setProperty("text", QStringLiteral("Loading")); - else - m_connectionInfo->setProperty("text", progress); - updateUI(); -} - -void MainWindow::setGeneratorDetails(const QString &filename) -{ - m_generatorInfo->setProperty("detailsText", filename); -} diff --git a/src/Viewer/Qt3DViewer/mainwindow.h b/src/Viewer/Qt3DViewer/mainwindow.h deleted file mode 100644 index 6a9f0612..00000000 --- a/src/Viewer/Qt3DViewer/mainwindow.h +++ /dev/null @@ -1,115 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 - 2016 NVIDIA Corporation. -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt 3D Studio. -** -** $QT_BEGIN_LICENSE:GPL$ -** 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 or (at your option) any later version -** approved by the KDE Free Qt Foundation. The licenses are as published by -** the Free Software Foundation and appearing in the file LICENSE.GPL3 -** 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. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include - -#include "remotedeploymentreceiver.h" - -QT_BEGIN_NAMESPACE -namespace Ui { -class MainWindow; -} -class QMimeData; -class Q3DSView; -QT_END_NAMESPACE - -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit MainWindow(bool generatorMode, QWidget *parent = nullptr); - ~MainWindow(); - - void loadFile(const QString &filename); - void loadProject(const QByteArray &data); - void updateProgress(int percent); - void setGeneratorDetails(const QString &filename); - -protected: - void dragEnterEvent(QDragEnterEvent *event) override; - void dropEvent(QDropEvent *event) override; - void showEvent(QShowEvent *event) override; - void closeEvent(QCloseEvent *event) override; -#ifdef Q_OS_ANDROID - void mousePressEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; -#endif - -private: - Q3DSView *viewer() const; - QString convertMimeDataToFilename(const QMimeData *mimeData); - void setupGeneratorUI(); - -public Q_SLOTS: - void generatorProgress(int totalFrames, int frameCount); - void generatorFinished(bool success, const QString &details); - -private Q_SLOTS: - void on_actionToggle_Scale_Mode_triggered(); - void on_actionToggle_Shade_Mode_triggered(); - void on_actionBorder_triggered(); - void on_actionFull_Screen_triggered(); - void on_actionShowOnScreenStats_triggered(); - void on_actionQuit_triggered(); - void on_actionOpen_triggered(); - void on_actionConnect_triggered(); - void on_actionReload_triggered(); - void on_actionCenter_triggered(); - void on_actionScale_To_Fit_triggered(); - void on_actionScale_To_Fill_triggered(); - - void updateUI(); - - void loadRemoteDeploymentReceiver(); - void remoteProjectChanging(); - void remoteConnected(); - void remoteDisconnected(); - void onErrorChanged(const QString &error); - -private: - Ui::MainWindow *ui; - Q3DSView *m_studio3D = nullptr; - QQuickItem *m_connectionInfo = nullptr; - QString m_openFileDir; - bool m_embeddedMode; - QPoint m_swipeStart; - RemoteDeploymentReceiver *m_remoteDeploymentReceiver = nullptr; - bool m_generatorMode; - QQuickItem *m_generatorInfo = nullptr; - QQuickItem *m_errorInfo = nullptr; -}; - -#endif // MAINWINDOW_H diff --git a/src/Viewer/Qt3DViewer/mainwindow.ui b/src/Viewer/Qt3DViewer/mainwindow.ui deleted file mode 100644 index cddbff72..00000000 --- a/src/Viewer/Qt3DViewer/mainwindow.ui +++ /dev/null @@ -1,221 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 1042 - 556 - - - - Qt 3D Viewer - - - - :/resources/images/icon_256x256.png:/resources/images/icon_256x256.png - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QQuickWidget::SizeRootObjectToView - - - - - - - - - 0 - 0 - 1042 - 21 - - - - - File - - - - - - - - - - View - - - - Set application scale mode (-scalemode [center,fit,fill] command line parameter) - - - Scale Mode - - - - - - - - - - - - - - - - Quit - - - Ctrl+Q - - - - - true - - - Full Screen - - - F11 - - - - - true - - - Show Matte - - - Ctrl+D - - - - - true - - - true - - - Show Render Statistics - - - F1 - - - true - - - - - Open... - - - Ctrl+O - - - - - true - - - Connect - - - - - Reload - - - - - true - - - Center - - - Center content and use design dimensions - - - - - true - - - Scale To Fit - - - Fit content to available space but maintain aspect ratio - - - - - true - - - Scale To Fill - - - Use entire screen space for content, changing aspect ratio as necessary - - - - - Toggle Scale Mode - - - Toggle between the various scale modes - - - Ctrl+Shift+S - - - - - Toggle Shade Mode - - - Ctrl+Shift+W - - - - - - - QQuickWidget - QWidget -
QtQuickWidgets/QQuickWidget
-
-
- - - - -
diff --git a/src/Viewer/Qt3DViewer/qml/StyledButton.qml b/src/Viewer/Qt3DViewer/qml/StyledButton.qml new file mode 100644 index 00000000..400179de --- /dev/null +++ b/src/Viewer/Qt3DViewer/qml/StyledButton.qml @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +Button { + id: control + implicitWidth: _controlBaseWidth + implicitHeight: _controlBaseHeight + + contentItem: Text { + width: _controlBaseWidth + text: control.text + height: _controlBaseHeight + font.pixelSize: _fontSize + color: _textColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + background: Rectangle { + color: control.down ? _menuSelectionColor : _dialogFieldColor + border.color: _dialogFieldBorderColor + radius: 2 + } +} diff --git a/src/Viewer/Qt3DViewer/qml/StyledMenu.qml b/src/Viewer/Qt3DViewer/qml/StyledMenu.qml new file mode 100644 index 00000000..6275dbc6 --- /dev/null +++ b/src/Viewer/Qt3DViewer/qml/StyledMenu.qml @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +Menu { + id: control + + property alias hovered: menuArea.containsMouse + + width: contentItem.width + leftPadding + rightPadding + height: contentItem.height + topPadding + bottomPadding + padding: 1 // For background border + x: 0 + y: parent.height + closePolicy: Popup.CloseOnPressOutsideParent | Popup.CloseOnEscape + + contentItem: MouseArea { + id: menuArea + hoverEnabled: true + height: list.height + width: list.width + ListView { + id: list + boundsBehavior: Flickable.StopAtBounds + clip: true + model: control.contentModel + currentIndex: control.currentIndex + highlightRangeMode: ListView.ApplyRange + highlightMoveDuration: 0 + Component.onCompleted: { + var maxItemWidth = 0; + var maxShortcutWidth = 0; + var totalHeight = 0 + var extraWidth = 0 + var i; + for (i = control.contentData.length - 1; i >= 0; --i) { + if (control.contentData[i].itemWidth !== undefined) { + maxItemWidth = Math.max(maxItemWidth, control.contentData[i].itemWidth); + maxShortcutWidth = Math.max(maxShortcutWidth, + control.contentData[i].shortcutWidth); + } + totalHeight += control.contentData[i].height + } + maxItemWidth += _controlPadding // minimum item spacer + for (i = control.contentData.length - 1; i >= 0; --i) { + if (control.contentData[i].itemSpacerWidth !== undefined) { + control.contentData[i].itemSpacerWidth + = maxItemWidth - control.contentData[i].itemWidth; + control.contentData[i].shortcutSpacerWidth + = maxShortcutWidth - control.contentData[i].shortcutWidth; + } + } + width = maxItemWidth + maxShortcutWidth + extraWidth + + control.contentData[0].leftPadding + control.contentData[0].rightPadding + + control.contentData[0].arrowWidth + control.contentData[0].checkMarkWidth; + height = totalHeight + } + } + } + + background: Rectangle { + width: control.width + height: control.height + color: _menuBackgroundColor + border.color: _menuBorderColor + } +} diff --git a/src/Viewer/Qt3DViewer/qml/StyledMenuButton.qml b/src/Viewer/Qt3DViewer/qml/StyledMenuButton.qml new file mode 100644 index 00000000..b5290c3d --- /dev/null +++ b/src/Viewer/Qt3DViewer/qml/StyledMenuButton.qml @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +Button { + id: control + + property Menu menu: null + property ApplicationWindow window: null + + onPressed: { + if (menu.visible) + menu.close(); + else + menu.open(); + } + + onHoveredChanged: { + if (hovered && window.menuOpen) { + window.closeMenus(); + menu.open(); + } + } + + hoverEnabled: true + width: contentItem.contentWidth + leftPadding + rightPadding + leftPadding: _controlPadding + rightPadding: _controlPadding + height: _controlBaseHeight + contentItem: Text { + text: control.text + font.pixelSize: _fontSize + opacity: enabled ? 1.0 : 0.3 + color: _textColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + background: Rectangle { + opacity: enabled ? 1 : 0.3 + color: control.down || control.hovered + ? _menuSelectionColor : _menuBackgroundColor + } +} diff --git a/src/Viewer/Qt3DViewer/qml/StyledMenuItem.qml b/src/Viewer/Qt3DViewer/qml/StyledMenuItem.qml new file mode 100644 index 00000000..79049a90 --- /dev/null +++ b/src/Viewer/Qt3DViewer/qml/StyledMenuItem.qml @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +MenuItem { + id: control + + property alias shortcut: shortcut.sequence + property string shortcutText + property alias itemSpacerWidth: itemSpacer.width + property alias shortcutSpacerWidth: shortcutSpacer.width + property alias itemWidth: itemLabel.width + property alias shortcutWidth: shortcutLabel.width + property bool showArrow: false + property int arrowWidth: arrow.width + property int checkMarkWidth: checkMark.width + property bool showCheckMark: false + property Menu arrowMenu: null + + hoverEnabled: true + width: contentItem.width + leftPadding + rightPadding + height: contentItem.height + topPadding + bottomPadding + padding: 0 + leftPadding: 0 + rightPadding: 0 + + MouseArea { + anchors.fill: parent + onPressed: { + if (showArrow) { + if (!arrowMenu.visible) { + arrowMenuDelay.stop(); + arrowMenu.open(); + } + } else { + mouse.accepted = false; + } + } + } + + onHoveredChanged: { + if (showArrow) + arrowMenuDelay.start(); + } + + Timer { + id: arrowMenuDelay + interval: 500 + repeat: false + onTriggered: { + if (arrowMenu.visible) { + if (!control.hovered && !arrowMenu.hovered) + arrowMenu.close(); + } else { + if (control.hovered) + arrowMenu.open(); + } + } + } + + Shortcut { + id: shortcut + context: Qt.ApplicationShortcut + onActivated: control.triggered() + } + + contentItem: Row { + width: checkMark.width + itemLabel.width + itemSpacer.width + + shortcutLabel.width + shortcutSpacer.width + arrow.width + height: _controlBaseHeight + Item { + id: checkMark + width: 16 + height: _controlBaseHeight + Image { + anchors.fill: parent + visible: control.showCheckMark + fillMode: Image.Pad + source: "qrc:/images/check.png" + } + } + Label { + id: itemLabel + text: control.text + font.pixelSize: _fontSize + horizontalAlignment: Text.AlignLeft + color: control.enabled ? _textColor : _disabledColor + verticalAlignment: Text.AlignVCenter + clip: true + width: contentWidth + height: _controlBaseHeight + } + Item { + id: itemSpacer + width: _controlPadding + height: _controlBaseHeight + } + Label { + id: shortcutLabel + text: shortcut.nativeText === "" ? control.shortcutText : shortcut.nativeText + font.pixelSize: _fontSize + horizontalAlignment: Text.AlignLeft + color: control.enabled ? _textColor : _disabledColor + verticalAlignment: Text.AlignVCenter + clip: true + width: contentWidth + height: _controlBaseHeight + } + Item { + id: shortcutSpacer + width: 0 + height: _controlBaseHeight + } + Item { + id: arrow + width: 16 + height: _controlBaseHeight + Image { + anchors.fill: parent + visible: control.showArrow + fillMode: Image.Pad + source: "qrc:/images/arrow.png" + } + } + } + background: Rectangle { + width: control.width + height: control.height + color: control.hovered ? _menuSelectionColor : _menuBackgroundColor + } +} diff --git a/src/Viewer/Qt3DViewer/qml/StyledMenuSeparator.qml b/src/Viewer/Qt3DViewer/qml/StyledMenuSeparator.qml new file mode 100644 index 00000000..344d6678 --- /dev/null +++ b/src/Viewer/Qt3DViewer/qml/StyledMenuSeparator.qml @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +MenuSeparator { + id: control + padding: 0 + topPadding: 0 + bottomPadding: 0 + leftPadding: 0 + rightPadding: 0 + width: parent.width + height: 1 + contentItem: Rectangle { + width: control.width - control.leftPadding - control.rightPadding + height: 1 + color: _menuBorderColor + } +} diff --git a/src/Viewer/Qt3DViewer/qml/main.qml b/src/Viewer/Qt3DViewer/qml/main.qml new file mode 100644 index 00000000..c983246c --- /dev/null +++ b/src/Viewer/Qt3DViewer/qml/main.qml @@ -0,0 +1,542 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Dialogs 1.2 +import Qt3DStudioViewer 1.0 +import QtStudio3D 1.0 +import QtQuick.Window 2.2 + +ApplicationWindow { + id: window + width: 1280 + height: 768 + visible: true + title: qsTr("Qt 3D Studio Viewer") + + property bool menuOpen: fileMenu.visible || viewMenu.visible + property Item loadedContent: contentLoader ? contentLoader.item : null + property string error + property int previousVisibility + + property color matteColor: Qt.rgba(0, 0, 0, 1) + property bool showRenderStats: false + property int scaleMode: ViewerSettings.ScaleModeCenter + + function closeMenus() { + fileMenu.close(); + scaleMenu.close(); + viewMenu.close(); + } + + Component.onCompleted: { + _viewerHelper.restoreWindowState(window); + previousVisibility = visibility; + } + + onClosing: { + _viewerHelper.storeWindowState(window); + } + + MouseArea { + property int swipeStart: 0 + + anchors.fill: parent + z: 10 + + onPressed: { + if (window.visibility === Window.FullScreen) + swipeStart = mouse.y; + _viewerHelper.handleMousePress(mouse.x, mouse.y, mouse.button, mouse.buttons, + mouse.modifiers); + mouse.accepted = true; + } + onReleased: { + _viewerHelper.handleMouseRelease(mouse.x, mouse.y, mouse.button, mouse.buttons, + mouse.modifiers); + mouse.accepted = true; + } + onPositionChanged: { + // Swipe down to exit fullscreen mode + if (window.visibility === Window.FullScreen && mouse.y > swipeStart + (height / 8)) { + window.visibility = window.previousVisibility; + } else { + _viewerHelper.handleMouseMove(mouse.x, mouse.y, mouse.button, mouse.buttons, + mouse.modifiers); + } + } + } + + DropArea { + anchors.fill: parent + onEntered: { + if (drag.hasUrls) { + var filename = _viewerHelper.convertUrlListToFilename(drag.urls); + if (filename === "") + drag.accepted = false; + } + } + onDropped: { + if (drop.hasUrls) { + var filename = _viewerHelper.convertUrlListToFilename(drop.urls); + if (filename === "") + drag.accepted = false; + else + _viewerHelper.loadFile(filename); + } + } + } + + Loader { + id: contentLoader + anchors.fill: parent + sourceComponent: { + switch (_viewerHelper.contentView) { + case ViewerHelper.StudioView: + return studioContent; + case ViewerHelper.ConnectView: + return connectContent; + case ViewerHelper.SequenceView: + return sequenceContent; + default: + return emptyContent; + } + } + Timer { + id: asyncContentChanger + repeat: false + interval: 0 + property int view: ViewerHelper.DefaultView + function changeView(newView) { + view = newView; + start(); + } + + onTriggered: { + _viewerHelper.contentView = view; + } + } + } + + Component { + id: emptyContent + Rectangle { + color: "black" + Label { + anchors.fill: parent + text: window.error + color: _textColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.pixelSize: width / 80 + } + } + } + + Component { + id: studioContent + Studio3D { + id: studio3D + + property alias hiderVisible: hider.visible + + focus: true + ViewerSettings { + matteColor: window.matteColor + showRenderStats: window.showRenderStats + scaleMode: window.scaleMode + } + + // Hider item keeps the Studio3D hidden until it starts running and we reset the + // animation time to the start + Rectangle { + id: hider + color: "black" + anchors.fill: parent + } + + Timer { + id: revealTimer + repeat: false + interval: 0 + onTriggered: { + hider.visible = false; + } + } + + onRunningChanged: { + if (running) { + // Successfully opened a presentation, update the open folder + _viewerHelper.openFolder = presentation.source.toString(); + // Force the animation to start from the beginning, as the first frame render + // can take some time as shaders are compiled on-demand + // Localization note: "Scene" needs to be the same as the default "Scene" + // element name generated in the Studio application. + presentation.goToTime(qsTr("Scene"), 0); + revealTimer.start(); + } + } + onErrorChanged: { + if (error.length > 0) { + window.error = error; + asyncContentChanger.changeView(ViewerHelper.DefaultView); + } + } + } + } + + Component { + id: connectContent + Rectangle { + color: "black" + Label { + anchors.fill: parent + text: _viewerHelper.connectText + color: _textColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.pixelSize: width / 40 + } + } + } + + Component { + id: sequenceContent + Rectangle { + property alias mainText: mainLabel.text + property alias detailsText: detailsLabel.text + color: "black" + Item { + anchors.fill: parent + Label { + id: mainLabel + color: _textColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignBottom + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottomMargin: _controlPadding + height: parent.height / 2 + font.pixelSize: width / 40 + text: qsTr("Image sequence generation initializing...") + } + Label { + id: detailsLabel + color: _textColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignTop + anchors.top: mainLabel.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.topMargin: _controlPadding + height: parent.height / 2 + font.pixelSize: width / 50 + } + } + } + } + + Rectangle { + id: ipEntry + visible: false + color: _dialogBackgroundColor + border.color: _dialogBorderColor + y: (parent.height - height) / 2 + x: (parent.width - width) / 2 + width: connectionEntry.width + (2 * _controlPadding) + height: connectionEntry.height + (2 * _controlPadding) + + onVisibleChanged: { + if (visible) { + connectText.forceActiveFocus(); + connectText.selectAll(); + } + } + + Grid { + id: connectionEntry + spacing: _controlPadding + columns: 2 + y: _controlPadding + x: _controlPadding + Label { + id: ipEntryLabel + width: _controlBaseWidth + height: _controlBaseHeight + text: qsTr("Enter IP port:") + color: _textColor + font.pixelSize: _fontSize + verticalAlignment: Text.AlignVCenter + padding: _controlPadding / 2 + } + TextField { + id: connectText + width: _controlBaseWidth + height: _controlBaseHeight + font.pixelSize: _fontSize + color: _textColor + selectByMouse: true + padding: _controlPadding / 6 + enabled: ipEntry.visible + text: _viewerHelper.connectPort + validator: IntValidator { + bottom: 1 + top: 65535 + } + + onAccepted: { + if (ipEntry.visible) { + _viewerHelper.contentView = ViewerHelper.ConnectView; + _viewerHelper.connectPort = Number(text); + _viewerHelper.connectRemote(); + ipEntry.visible = false; + } + } + + background: Rectangle { + id: textBackground + color: _dialogFieldColor + border.width: 1 + border.color: _dialogFieldBorderColor + radius: 2 + } + } + StyledButton { + id: connectButton + width: _controlBaseWidth + text: qsTr("Connect") + onClicked: { + _viewerHelper.contentView = ViewerHelper.ConnectView; + _viewerHelper.connectRemote(); + ipEntry.visible = false; + } + } + StyledButton { + id: cancelButton + width: _controlBaseWidth + text: qsTr("Cancel") + onClicked: { + ipEntry.visible = false; + } + } + } + } + + Component { + id: fileDialogComponent + FileDialog { + id: fileDialog + title: qsTr("Choose Presentation or Project") + folder: _viewerHelper.openFolder + nameFilters: [qsTr("All supported formats (*.uip *.uia *.uiab)"), + qsTr("Studio UI Presentation (*.uip)"), + qsTr("Application file (*.uia)"), + qsTr("Binary Application (*.uiab)")] + onAccepted: { + _viewerHelper.contentView = ViewerHelper.StudioView; + contentLoader.item.presentation.setSource(fileUrls[0]); + } + } + } + + header: Rectangle { + height: _controlBaseHeight + color: _menuBackgroundColor + visible: window.visibility !== Window.FullScreen + + Row { + anchors.fill: parent + StyledMenuButton { + id: fileButton + text: qsTr("File") + menu: fileMenu + window: window + + StyledMenu { + id: fileMenu + StyledMenuItem { + text: qsTr("Open...") + shortcut: StandardKey.Open + enabled: _viewerHelper.contentView !== ViewerHelper.SequenceView + onTriggered: { + if (enabled) { + fileDialogLoader.sourceComponent = fileDialogComponent; + fileDialogLoader.item.open(); + } + } + Loader { + id: fileDialogLoader + sourceComponent: Item {} + } + } + StyledMenuItem { + text: qsTr("Connect") + shortcut: "F9" + enabled: _viewerHelper.contentView !== ViewerHelper.SequenceView + showCheckMark: _viewerHelper.connected + onTriggered: { + if (enabled) + ipEntry.visible = !ipEntry.visible; + } + } + StyledMenuItem { + text: qsTr("Reload") + enabled: _viewerHelper.contentView === ViewerHelper.StudioView + shortcut: "F5" + onTriggered: { + if (enabled) { + contentLoader.item.hiderVisible = true; + contentLoader.item.reset(); + } + } + } + StyledMenuSeparator {} + StyledMenuItem { + text: qsTr("Quit") + shortcut: "Ctrl+Q" + onTriggered: { + window.close(); + } + } + } + } + StyledMenuButton { + id: viewButton + text: qsTr("View") + menu: viewMenu + window: window + + StyledMenu { + id: viewMenu + StyledMenuItem { + text: qsTr("Show Matte") + shortcut: "F6" + enabled: _viewerHelper.contentView === ViewerHelper.StudioView + onTriggered: { + if (enabled) { + if (window.matteColor === Qt.rgba(0, 0, 0, 1)) { + window.matteColor = Qt.rgba(0.2, 0.2, 0.2, 1); + showCheckMark = true; + } else { + window.matteColor = Qt.rgba(0, 0, 0, 1); + showCheckMark = false; + } + } + } + } + StyledMenuItem { + id: scaleMenuItem + text: qsTr("Scale Mode") + showArrow: true + arrowMenu: scaleMenu + enabled: _viewerHelper.contentView === ViewerHelper.StudioView + StyledMenu { + id: scaleMenu + x: parent.width + y: 0 + function clearChecks() { + scaleCenter.showCheckMark = false; + scaleFit.showCheckMark = false; + scaleFill.showCheckMark = false; + } + + StyledMenuItem { + id: scaleCenter + text: qsTr("Center") + shortcut: "F2" + showCheckMark: true + enabled: _viewerHelper.contentView === ViewerHelper.StudioView + onTriggered: { + if (enabled) { + scaleMenu.clearChecks(); + window.scaleMode = ViewerSettings.ScaleModeCenter; + showCheckMark = true; + } + } + } + StyledMenuItem { + id: scaleFit + text: qsTr("Scale to Fit") + shortcut: "F3" + enabled: _viewerHelper.contentView === ViewerHelper.StudioView + onTriggered: { + if (enabled) { + scaleMenu.clearChecks(); + window.scaleMode = ViewerSettings.ScaleModeFit; + showCheckMark = true; + } + } + } + StyledMenuItem { + id: scaleFill + text: qsTr("Scale to Fill") + shortcut: "F4" + enabled: _viewerHelper.contentView === ViewerHelper.StudioView + onTriggered: { + if (enabled) { + scaleMenu.clearChecks(); + window.scaleMode = ViewerSettings.ScaleModeFill; + showCheckMark = true; + } + } + } + } + } + StyledMenuItem { + text: qsTr("Show Render Statistics") + shortcut: "F1" + enabled: _viewerHelper.contentView === ViewerHelper.StudioView + onTriggered: { + if (enabled) { + window.showRenderStats = + !contentLoader.item.viewerSettings.showRenderStats; + showCheckMark = window.showRenderStats; + } + } + } + StyledMenuSeparator {} + StyledMenuItem { + text: qsTr("Full Screen") + shortcut: "F11" + onTriggered: { + if (window.visibility !== Window.FullScreen) { + window.previousVisibility = window.visibility + window.visibility = Window.FullScreen; + } else { + window.visibility = window.previousVisibility; + } + } + } + } + } + } + } +} diff --git a/src/Viewer/Qt3DViewer/remotedeploymentreceiver.cpp b/src/Viewer/Qt3DViewer/remotedeploymentreceiver.cpp index afe05030..81fb488c 100644 --- a/src/Viewer/Qt3DViewer/remotedeploymentreceiver.cpp +++ b/src/Viewer/Qt3DViewer/remotedeploymentreceiver.cpp @@ -28,34 +28,19 @@ ****************************************************************************/ #include "remotedeploymentreceiver.h" -#include "mainwindow.h" +#include "viewer.h" #include -#include -RemoteDeploymentReceiver::RemoteDeploymentReceiver(QWidget *parent) +RemoteDeploymentReceiver::RemoteDeploymentReceiver(int serverPort, QObject *parent) : QObject(parent) - , m_mainWindow(parent) , m_tcpServer(0) , m_connection(0) , m_temporaryDir(0) - , m_serverPort(36000) + , m_serverPort(serverPort) , m_projectDeployed(false) - , m_canceled(false) { m_incoming.setVersion(QDataStream::Qt_5_8); - - QInputDialog inputPort; - inputPort.setLabelText(tr("Enter IP Port:")); - inputPort.setInputMode(QInputDialog::IntInput); - inputPort.setIntMinimum(0); - inputPort.setIntMaximum(65536); - inputPort.setIntValue(m_serverPort); - inputPort.setWindowFlags(Qt::Popup); - // We don't have to worry about cancel with this, as the dialog is always recreated when - // initializing the connection. - connect(&inputPort, &QInputDialog::intValueSelected, this, &RemoteDeploymentReceiver::setPort); - m_canceled = (inputPort.exec() != 1); } RemoteDeploymentReceiver::~RemoteDeploymentReceiver() @@ -69,18 +54,18 @@ void RemoteDeploymentReceiver::setPort(int value) m_serverPort = value; } -bool RemoteDeploymentReceiver::startServer() +QString RemoteDeploymentReceiver::startServer() { if (m_tcpServer) - return true; + return QString(); m_tcpServer = new QTcpServer(this); if (!m_tcpServer->listen(QHostAddress::Any, m_serverPort)) { - qWarning() << "Can't start the remote connection: " - << m_tcpServer->errorString(); + QString error = tr("Can't start the remote connection: '%1'") + .arg(m_tcpServer->errorString()); delete m_tcpServer; m_tcpServer = 0; - return false; + return error; } QList ipAddressesList = QNetworkInterface::allAddresses(); @@ -100,7 +85,7 @@ bool RemoteDeploymentReceiver::startServer() m_serverPort = m_tcpServer->serverPort(); connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(acceptRemoteConnection())); - return true; + return QString(); } void RemoteDeploymentReceiver::acceptRemoteConnection() @@ -143,8 +128,11 @@ void RemoteDeploymentReceiver::readProject() int totalBytes = 0; m_incoming >> totalBytes; - qobject_cast(m_mainWindow)->updateProgress( - 100 * ((double)m_connection->bytesAvailable() / (double)totalBytes)); + Viewer *viewer = qobject_cast(parent()); + if (viewer && totalBytes != 0) { + viewer->updateProgress( + 100 * ((double)m_connection->bytesAvailable() / (double)totalBytes)); + } if (m_connection->bytesAvailable() < totalBytes) { m_incoming.rollbackTransaction(); diff --git a/src/Viewer/Qt3DViewer/remotedeploymentreceiver.h b/src/Viewer/Qt3DViewer/remotedeploymentreceiver.h index 51b485ae..95744808 100644 --- a/src/Viewer/Qt3DViewer/remotedeploymentreceiver.h +++ b/src/Viewer/Qt3DViewer/remotedeploymentreceiver.h @@ -35,16 +35,15 @@ #include #include #include -#include class RemoteDeploymentReceiver : public QObject { Q_OBJECT public: - explicit RemoteDeploymentReceiver(QWidget *parent); + explicit RemoteDeploymentReceiver(int serverPort, QObject *parent); ~RemoteDeploymentReceiver(); - bool startServer(); + QString startServer(); QHostAddress hostAddress() const { return m_hostAddress; } int serverPort() const { return m_serverPort; } @@ -52,7 +51,6 @@ public: bool isConnected() const { return m_connection; } bool isProjectDeployed() const { return m_connection && m_projectDeployed; } QString fileName() const { return m_projectFile; } - bool canceled() const { return m_canceled; } Q_SIGNALS: void projectChanged(); @@ -67,7 +65,6 @@ private Q_SLOTS: void setPort(int value); private: - QWidget *m_mainWindow = nullptr; QTcpServer *m_tcpServer = nullptr; QTcpSocket *m_connection = nullptr; QHostAddress m_hostAddress; @@ -76,7 +73,6 @@ private: QString m_projectFile; bool m_projectDeployed; int m_serverPort; - bool m_canceled; }; #endif // REMOTEDEPLOYMENTRECEIVER_H diff --git a/src/Viewer/Qt3DViewer/resources/images/ViewerDoc.ico b/src/Viewer/Qt3DViewer/resources/images/ViewerDoc.ico deleted file mode 100644 index 02779b0d..00000000 Binary files a/src/Viewer/Qt3DViewer/resources/images/ViewerDoc.ico and /dev/null differ diff --git a/src/Viewer/Qt3DViewer/resources/images/arrow.png b/src/Viewer/Qt3DViewer/resources/images/arrow.png new file mode 100644 index 00000000..40ebda88 Binary files /dev/null and b/src/Viewer/Qt3DViewer/resources/images/arrow.png differ diff --git a/src/Viewer/Qt3DViewer/resources/images/arrow@2x.png b/src/Viewer/Qt3DViewer/resources/images/arrow@2x.png new file mode 100644 index 00000000..1a21ee06 Binary files /dev/null and b/src/Viewer/Qt3DViewer/resources/images/arrow@2x.png differ diff --git a/src/Viewer/Qt3DViewer/resources/images/check.png b/src/Viewer/Qt3DViewer/resources/images/check.png new file mode 100644 index 00000000..5c1ef70a Binary files /dev/null and b/src/Viewer/Qt3DViewer/resources/images/check.png differ diff --git a/src/Viewer/Qt3DViewer/resources/images/check@2x.png b/src/Viewer/Qt3DViewer/resources/images/check@2x.png new file mode 100644 index 00000000..ed730bfe Binary files /dev/null and b/src/Viewer/Qt3DViewer/resources/images/check@2x.png differ diff --git a/src/Viewer/Qt3DViewer/resources/images/icon_256x256.png b/src/Viewer/Qt3DViewer/resources/images/icon_256x256.png deleted file mode 100644 index 0c7bf48b..00000000 Binary files a/src/Viewer/Qt3DViewer/resources/images/icon_256x256.png and /dev/null differ diff --git a/src/Viewer/Qt3DViewer/resources/images/icon_256x256@2x.png b/src/Viewer/Qt3DViewer/resources/images/icon_256x256@2x.png deleted file mode 100644 index 29a8ed75..00000000 Binary files a/src/Viewer/Qt3DViewer/resources/images/icon_256x256@2x.png and /dev/null differ diff --git a/src/Viewer/Qt3DViewer/resources/wordlist/wordlist.txt b/src/Viewer/Qt3DViewer/resources/wordlist/wordlist.txt deleted file mode 100644 index 59afadae..00000000 --- a/src/Viewer/Qt3DViewer/resources/wordlist/wordlist.txt +++ /dev/null @@ -1,81 +0,0 @@ -_ENV -_VERSION -assert -break -calculateBoundingbox -calculateGlobalTransform -collectgarbage -dofile -else -elseif -error -fireEvent -fireEventOnPresentation -fireFlowEvent -function -getAttribute -getChildren -getcurrentSlide -getElapsedTime -getElement -getElementType -getfenv -getmetatable -getMousePosition -getScreenInfo -getTime -goToBackSlide -goToNextSlide -goToPreviousSlide -goToSlide -goToTime -hasAttribute -io.stderr -io.stdin -io.stdout -ipairs -load -loadfile -loadstring -local -math.huge -math.pi -measureText -module -next -output -package.config -package.cpath -package.loaded -package.loaders -package.path -package.preload -package.searchers -pairs -payse -pcall -play -print -rawequal -rawget -rawlen -rawset -registerForChange -registerForEvent -repeat -require -return -select -self -setAttribute -setfenv -setmetatable -then -tonumber -tostring -type -unpack -unregisterForEvent -until -while -xpcall \ No newline at end of file diff --git a/src/Viewer/Qt3DViewer/style_android.qss b/src/Viewer/Qt3DViewer/style_android.qss deleted file mode 100644 index bd92364b..00000000 --- a/src/Viewer/Qt3DViewer/style_android.qss +++ /dev/null @@ -1,130 +0,0 @@ -/* General coloring and font size */ -QWidget { - color: #ffffff; - background: #2e2f30; - font-size: 20px; - border: 0px; -} - -/* Scrollbar */ -QScrollBar:horizontal { - border: 1px solid #262829; - background: #404244; - height: 40px; -} - -QScrollBar::handle:horizontal { - background: #727476; -} - -QScrollBar::add-line:horizontal, -QScrollBar::sub-line:horizontal { - width: 0px; -} - -QScrollBar:vertical { - border: 1px solid #262829; - background: #404244; - width: 40px; -} - -QScrollBar::handle:vertical { - background: #727476; -} - -QScrollBar::add-line:vertical, -QScrollBar::sub-line:vertical { - height: 0px; -} - -/* Toolbar */ -QToolBar { - min-height: 40px; - max-height: 40px; -} - -QToolButton:!checked { - background-color: #404244; - margin-bottom: 9px; -} - -QToolButton:checked { - background-color: #262829; - margin-bottom: 9px; -} - -QToolButton:pressed { - background-color: #46a2da; - margin-bottom: 9px; -} - -/* Dialog widgets */ -QComboBox, -QLineEdit { - background: #404244; - border: 1px solid #262829; - border-radius: 2; - height: 40px; - padding: 3px; - margin-bottom: 9px; -} - -QSpinBox { - background: #404244; - border: 11px solid #262829; - border-radius: 2; - height: 60px; - padding: -10px; -} - -QSpinBox::up-button { - background: #404244; - width: 40px; - padding: 10px; - border: 0px; - margin-top: -10px; - margin-right: -10px; -} - -QSpinBox::down-button -{ - background: #404244; - width: 40px; - padding: 10px; - border: 0px; - margin-bottom: -10px; - margin-right: -10px; -} - -QComboBox::drop-down { - background: #404244; - width: 40px; - border: 0px; -} - -QComboBox::down-arrow, -QSpinBox::down-arrow { - image: url(:/images/arrow_down.png); -} - -QSpinBox::up-arrow { - image: url(:/images/arrow_up.png); -} - -QPushButton { - background: #404244; - border: 1px solid #262829; - border-radius: 2; - padding: 3px 10px 3px 10px; - min-height: 40px; - min-width: 75px; - margin-bottom: 9px; -} - -QPushButton::pressed { - background-color: #46a2da; -} - -QFileDialog { - min-width: 850px; -} diff --git a/src/Viewer/Qt3DViewer/viewer.cpp b/src/Viewer/Qt3DViewer/viewer.cpp new file mode 100644 index 00000000..871f9654 --- /dev/null +++ b/src/Viewer/Qt3DViewer/viewer.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "viewer.h" +#include "q3dspresentationitem.h" + +Viewer::Viewer(bool generatorMode, QObject *parent) + : QObject(parent) + , m_generatorMode(generatorMode) +{ + if (m_generatorMode) + setContentView(SequenceView); +} + +Viewer::~Viewer() +{ +} + +void Viewer::connectRemote() +{ + if (m_remoteDeploymentReceiver) { + delete m_remoteDeploymentReceiver; + m_remoteDeploymentReceiver = 0; + Q_EMIT connectedChanged(); + } + + m_remoteDeploymentReceiver = new RemoteDeploymentReceiver(m_connectPort, this); + QString error = m_remoteDeploymentReceiver->startServer(); + if (!error.isEmpty()) { + delete m_remoteDeploymentReceiver; + m_remoteDeploymentReceiver = nullptr; + setContentView(DefaultView); + m_qmlRootObject->setProperty("error", QVariant(error)); + return; + } + + m_connectText.clear(); + QTextStream stream(&m_connectText); + stream << tr("Connection Info\nAddress: %1\nPort: %2") + .arg(m_remoteDeploymentReceiver->hostAddress().toString()) + .arg(QString::number(m_connectPort)); + Q_EMIT connectTextChanged(); + + connect(m_remoteDeploymentReceiver, &RemoteDeploymentReceiver::remoteConnected, + this, &Viewer::remoteConnected); + + connect(m_remoteDeploymentReceiver, &RemoteDeploymentReceiver::remoteDisconnected, + this, &Viewer::remoteDisconnected); + + connect(m_remoteDeploymentReceiver, &RemoteDeploymentReceiver::projectChanging, + this, &Viewer::remoteProjectChanging); + + connect(m_remoteDeploymentReceiver, &RemoteDeploymentReceiver::projectChanged, + this, &Viewer::loadRemoteDeploymentReceiver); + + Q_EMIT connectedChanged(); +} + +// Used to load files via command line +void Viewer::loadFile(const QString &filename) +{ + QFileInfo fileInfo(filename); + if (!fileInfo.exists()) { + setContentView(DefaultView); + m_qmlRootObject->setProperty( + "error", QVariant(tr("Tried to load nonexistent file %1").arg(filename))); + return; + } + + QUrl sourceUrl = QUrl::fromLocalFile(fileInfo.absoluteFilePath()); + + setContentView(StudioView); + + if (qmlStudio()) + qmlStudio()->presentation()->setSource(sourceUrl); +} + +QString Viewer::convertUrlListToFilename(const QList &list) +{ + for (const QUrl &url : list) { + QString str = url.toLocalFile(); + if (str.isEmpty() == false) { + if ((QFileInfo(str).suffix() == "uip") + || (QFileInfo(str).suffix() == "uia") + || (QFileInfo(str).suffix() == "uiab")) + { + return str; + } + } + } + return QString(); +} + +void Viewer::restoreWindowState(QWindow *window) +{ + QSettings settings; + QRect geo = settings.value(QStringLiteral("WindowGeometry"), QRect()).toRect(); + int visibility = settings.value(QStringLiteral("WindowVisibility"), + QWindow::Windowed).toInt(); + + if (!geo.isNull()) { + // The first geometry set at startup may adjust the geometry according to pixel + // ratio if mouse cursor is on different screen than where window goes and the + // two screens have different pixel ratios. Setting geometry twice seems to + // work around this. + geo.adjust(0, 0, -1, 0); + window->setGeometry(geo); + geo.adjust(0, 0, 1, 0); + window->setGeometry(geo); + } + + window->setVisibility(QWindow::Visibility(visibility)); +} + +void Viewer::storeWindowState(QWindow *window) +{ + QSettings settings; + settings.setValue(QStringLiteral("WindowGeometry"), window->geometry()); + settings.setValue(QStringLiteral("WindowState"), window->visibility()); +} + +// Since we need mouse events for handling the swipe gesture in viewer, we need to generate +// a fake event for the presentation. +void Viewer::handleMousePress(int x, int y, int button, int buttons, int modifiers) +{ + if (qmlStudio()) { + QMouseEvent fakeEvent(QEvent::MouseButtonPress, + QPointF(x, y) * qmlStudio()->window()->devicePixelRatio(), + Qt::MouseButton(button), + Qt::MouseButtons(buttons), + Qt::KeyboardModifiers(modifiers)); + qmlStudio()->presentation()->mousePressEvent(&fakeEvent); + } +} + +void Viewer::handleMouseRelease(int x, int y, int button, int buttons, int modifiers) +{ + if (qmlStudio()) { + QMouseEvent fakeEvent(QEvent::MouseButtonRelease, + QPointF(x, y) * qmlStudio()->window()->devicePixelRatio(), + Qt::MouseButton(button), + Qt::MouseButtons(buttons), + Qt::KeyboardModifiers(modifiers)); + qmlStudio()->presentation()->mouseReleaseEvent(&fakeEvent); + } +} + +void Viewer::handleMouseMove(int x, int y, int button, int buttons, int modifiers) +{ + if (qmlStudio()) { + QMouseEvent fakeEvent(QEvent::MouseMove, + QPointF(x, y) * qmlStudio()->window()->devicePixelRatio(), + Qt::MouseButton(button), + Qt::MouseButtons(buttons), + Qt::KeyboardModifiers(modifiers)); + qmlStudio()->presentation()->mouseMoveEvent(&fakeEvent); + } +} + +void Viewer::setContentView(Viewer::ContentView view) +{ + if (view != m_contentView) { + m_qmlStudio = nullptr; + m_contentView = view; + Q_EMIT contentViewChanged(); + } +} + +Viewer::ContentView Viewer::contentView() const +{ + return m_contentView; +} + +void Viewer::setOpenFolder(const QUrl &folder) +{ + QString localFolder = folder.toLocalFile(); + QFileInfo fi(localFolder); + QString newDir; + if (fi.isDir()) + newDir = fi.absoluteFilePath(); + else + newDir = fi.absolutePath(); + if (newDir != m_openFileDir) { + m_openFileDir = newDir; + QSettings settings; + settings.setValue(QStringLiteral("DirectoryOfLastOpen"), m_openFileDir); + Q_EMIT openFolderChanged(); + } +} + +QUrl Viewer::openFolder() +{ + if (m_openFileDir.size() == 0) { + QSettings settings; + m_openFileDir = settings.value(QStringLiteral("DirectoryOfLastOpen"), + QString("")).toString(); +#ifdef Q_OS_ANDROID + if (m_openFileDir.isEmpty()) + m_openFileDir = QStringLiteral("/sdcard/qt3dviewer"); // Add default folder for Android +#endif + } + return QUrl::fromLocalFile(m_openFileDir); +} + +void Viewer::setConnectPort(int port) +{ + if (m_connectPort != port) { + QSettings settings; + m_connectPort = port; + settings.setValue(QStringLiteral("ConnectPort"), m_connectPort); + Q_EMIT connectPortChanged(); + } +} + +int Viewer::connectPort() +{ + if (m_connectPort < 0) { + QSettings settings; + m_connectPort = settings.value(QStringLiteral("ConnectPort"), 36000).toInt(); + } + return m_connectPort; +} + +QString Viewer::connectText() const +{ + return m_connectText; +} + +bool Viewer::isConnected() const +{ + return m_remoteDeploymentReceiver ? m_remoteDeploymentReceiver->isConnected() : false; +} + +void Viewer::setQmlRootObject(QObject *obj) +{ + m_qmlRootObject = obj; +} + +void Viewer::loadRemoteDeploymentReceiver() +{ + Q_ASSERT(m_remoteDeploymentReceiver); + const QString remote = m_remoteDeploymentReceiver->fileName(); + QMetaObject::invokeMethod(this, "loadFile", Qt::QueuedConnection, Q_ARG(QString, remote)); +} + +void Viewer::remoteProjectChanging() +{ + if (m_contentView != ConnectView) + setContentView(ConnectView); + m_connectText = tr("Loading remote project..."); + Q_EMIT connectTextChanged(); +} + +void Viewer::remoteConnected() +{ + m_connectText = tr("Remote Connected"); + Q_EMIT connectTextChanged(); + Q_EMIT connectedChanged(); +} + +void Viewer::remoteDisconnected() +{ + m_connectText = tr("Remote Disconnected"); + Q_EMIT connectTextChanged(); + Q_EMIT connectedChanged(); +} + +Q3DSView *Viewer::qmlStudio() +{ + if (m_contentView == StudioView) { + if (!m_qmlStudio) { + QObject *loadedContent = m_qmlRootObject->property("loadedContent").value(); + m_qmlStudio = static_cast(loadedContent); + } + } else { + m_qmlStudio = nullptr; + } + return m_qmlStudio; +} + +void Viewer::generatorProgress(int totalFrames, int frameCount) +{ + QString progressString; + if (frameCount >= totalFrames) { + progressString = + QCoreApplication::translate( + "main", "Image sequence generation done! (%2 frames generated)") + .arg(totalFrames); + } else { + progressString = + QCoreApplication::translate("main", "Image sequence generation progress: %1 / %2") + .arg(frameCount).arg(totalFrames); + } + QObject *loadedContent = m_qmlRootObject->property("loadedContent").value(); + loadedContent->setProperty("mainText", progressString); +} + +void Viewer::generatorFinished(bool success, const QString &details) +{ + QObject *loadedContent = m_qmlRootObject->property("loadedContent").value(); + if (success) { + loadedContent->setProperty("detailsText", details); + } else { + QString mainString = + QCoreApplication::translate("main", "Image sequence generation failed:"); + loadedContent->setProperty("mainText", mainString); + loadedContent->setProperty("detailsText", details); + } +} + +void Viewer::updateProgress(int percent) +{ + // Don't wait for 100%, as it'll already start loading and text isn't updated anymore + if (percent >= 99) + m_connectText = tr("Loading remote project..."); + else + m_connectText = QStringLiteral("Receiving (%1%)").arg(percent); + Q_EMIT connectTextChanged(); +} + +void Viewer::setGeneratorDetails(const QString &filename) +{ + QObject *loadedContent = m_qmlRootObject->property("loadedContent").value(); + loadedContent->setProperty("detailsText", filename); +} diff --git a/src/Viewer/Qt3DViewer/viewer.h b/src/Viewer/Qt3DViewer/viewer.h new file mode 100644 index 00000000..7114728f --- /dev/null +++ b/src/Viewer/Qt3DViewer/viewer.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VIEWER_H +#define VIEWER_H + +#include "remotedeploymentreceiver.h" + +#include +#include +#include +#include "Qt3DSView.h" + +class Viewer : public QObject +{ + Q_OBJECT + + Q_PROPERTY(ContentView contentView READ contentView WRITE setContentView NOTIFY contentViewChanged) + Q_PROPERTY(QUrl openFolder READ openFolder WRITE setOpenFolder NOTIFY openFolderChanged) + Q_PROPERTY(int connectPort READ connectPort WRITE setConnectPort NOTIFY connectPortChanged) + Q_PROPERTY(QString connectText READ connectText NOTIFY connectTextChanged) + Q_PROPERTY(bool connected READ isConnected NOTIFY connectedChanged) + +public: + enum ContentView { + DefaultView, + StudioView, + ConnectView, + SequenceView + }; + + Q_ENUM(ContentView) + + explicit Viewer(bool generatorMode, QObject *parent = nullptr); + ~Viewer(); + + Q_INVOKABLE void connectRemote(); + Q_INVOKABLE void loadFile(const QString &filename); + Q_INVOKABLE QString convertUrlListToFilename(const QList &list); + Q_INVOKABLE void restoreWindowState(QWindow *window); + Q_INVOKABLE void storeWindowState(QWindow *window); + + Q_INVOKABLE void handleMousePress(int x, int y, int button, int buttons, int modifiers); + Q_INVOKABLE void handleMouseRelease(int x, int y, int button, int buttons, int modifiers); + Q_INVOKABLE void handleMouseMove(int x, int y, int button, int buttons, int modifiers); + + void setContentView(ContentView view); + ContentView contentView() const; + void setOpenFolder(const QUrl &folder); + QUrl openFolder(); // not const since it potentially updates from settings + void setConnectPort(int port); + int connectPort(); // not const since it potentially updates from settings + QString connectText() const; + bool isConnected() const; + QRect initialRect() const; + int initialState() const; + + void setQmlRootObject(QObject *obj); + + void loadProject(const QByteArray &data); + void updateProgress(int percent); + void setGeneratorDetails(const QString &filename); + +public Q_SLOTS: + void generatorProgress(int totalFrames, int frameCount); + void generatorFinished(bool success, const QString &details); + +private Q_SLOTS: + void loadRemoteDeploymentReceiver(); + void remoteProjectChanging(); + void remoteConnected(); + void remoteDisconnected(); + +Q_SIGNALS: + void contentViewChanged(); + void openFolderChanged(); + void connectPortChanged(); + void connectTextChanged(); + void connectedChanged(); + +private: + Q3DSView *qmlStudio(); + + QString m_openFileDir; + RemoteDeploymentReceiver *m_remoteDeploymentReceiver = nullptr; + bool m_generatorMode = false; + ContentView m_contentView = DefaultView; + QObject *m_qmlRootObject = nullptr; + int m_connectPort = -1; + QString m_connectText; + Q3DSView *m_qmlStudio = nullptr; +}; + +#endif // VIEWER_H diff --git a/src/Viewer/Qt3DViewer/viewer.qml b/src/Viewer/Qt3DViewer/viewer.qml deleted file mode 100644 index 32025907..00000000 --- a/src/Viewer/Qt3DViewer/viewer.qml +++ /dev/null @@ -1,39 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt 3D Studio. -** -** $QT_BEGIN_LICENSE:GPL$ -** 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 or (at your option) any later version -** approved by the KDE Free Qt Foundation. The licenses are as published by -** the Free Software Foundation and appearing in the file LICENSE.GPL3 -** 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. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -import QtQuick 2.7 -import QtQuick.Controls 2.2 -import QtStudio3D 1.0 - -Rectangle { - id: window - visible: true - color: "black" -} -- cgit v1.2.3