File indexing completed on 2024-05-12 05:36:48

0001 /*
0002  SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk>
0003  SPDX-FileCopyrightText: 2023 David Edmundson <davidedmundson@kde.org>
0004 
0005  SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include <QApplication>
0009 #include <QCommandLineOption>
0010 #include <QCommandLineParser>
0011 #include <QCoreApplication>
0012 #include <QDir>
0013 #include <QQmlAbstractUrlInterceptor>
0014 #include <QQmlComponent>
0015 #include <QQmlContext>
0016 #include <QQmlEngine>
0017 #include <QQuickItem>
0018 #include <QQuickView>
0019 
0020 #include <QScopedPointer>
0021 #include <QStringList>
0022 #include <QTimer>
0023 
0024 #include <KDirWatch>
0025 #include <KLocalizedContext>
0026 #include <KLocalizedString>
0027 
0028 #include <PlasmaQuick/QuickViewSharedEngine>
0029 #include <PlasmaQuick/SharedQmlEngine>
0030 
0031 #include <stdio.h>
0032 
0033 void noFilesGiven()
0034 {
0035     printf("kqml: No files specified. Terminating.\n");
0036     exit(1);
0037 }
0038 
0039 class QmlFileChangeWatcher : public QObject, public QQmlAbstractUrlInterceptor
0040 {
0041     Q_OBJECT
0042 public:
0043     QmlFileChangeWatcher(std::shared_ptr<QQmlEngine>, QObject *parent);
0044     QUrl intercept(const QUrl &path, DataType type) override;
0045 
0046 Q_SIGNALS:
0047     void dirty();
0048 
0049 private:
0050     void init();
0051     std::unique_ptr<KDirWatch> m_fileWatcher;
0052     QTimer m_resetTimer;
0053 };
0054 
0055 int main(int argc, char *argv[])
0056 {
0057     QApplication app(argc, argv);
0058 
0059     app.setApplicationName("KDE QML Runtime");
0060     app.setOrganizationName("KDE");
0061     app.setOrganizationDomain("kde.org");
0062     QApplication::setApplicationVersion(QLatin1String(PROJECT_VERSION));
0063 
0064     QStringList files;
0065 
0066     // Handle main arguments
0067     QCommandLineParser parser;
0068     parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
0069     parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsPositionalArguments);
0070     parser.addHelpOption();
0071     parser.addVersionOption();
0072 
0073     QCommandLineOption applicationDomainOption(QStringList{QStringLiteral("d"), QStringLiteral("domain")},
0074                                                QStringLiteral("The main localization domain"),
0075                                                QStringLiteral("domain"));
0076     parser.addOption(applicationDomainOption);
0077 
0078     // Positional arguments
0079     parser.addPositionalArgument("files", QStringLiteral("Any number of QML files can be loaded. They will share the same engine."), "[files...]");
0080     parser.addPositionalArgument("args",
0081                                  QStringLiteral("Arguments after '--' are ignored, but passed through to the application.arguments variable in QML."),
0082                                  "[-- args...]");
0083 
0084     parser.process(app);
0085 
0086     QString applicationDomain;
0087     if (parser.isSet(applicationDomainOption)) {
0088         applicationDomain = parser.value(applicationDomainOption);
0089     } else {
0090         applicationDomain = QStringLiteral("kqml");
0091     }
0092 
0093     for (QString posArg : parser.positionalArguments()) {
0094         if (posArg == QLatin1String("--")) {
0095             break;
0096         } else {
0097             files.append(posArg);
0098         }
0099     }
0100 
0101     if (files.size() <= 0) {
0102         noFilesGiven();
0103     }
0104 
0105     auto engine = PlasmaQuick::SharedQmlEngine().engine();
0106     auto watcher = new QmlFileChangeWatcher(engine, &app);
0107     QObject::connect(watcher, &QmlFileChangeWatcher::dirty, [&engine, files]() {
0108         engine->clearComponentCache();
0109         // engine->clearSingletons();
0110     });
0111 
0112     // Load files
0113     for (const QString &path : std::as_const(files)) {
0114         QUrl url = QUrl::fromUserInput(path, QDir::currentPath(), QUrl::AssumeLocalFile);
0115         auto window = new PlasmaQuick::QuickViewSharedEngine();
0116         window->setTranslationDomain(applicationDomain);
0117         window->setTitle(url.fileName());
0118         window->setSource(url);
0119         window->show();
0120 
0121         QObject::connect(watcher, &QmlFileChangeWatcher::dirty, window, [window]() {
0122             window->setSource(QUrl());
0123             delete window->rootObject();
0124         });
0125 
0126         QObject::connect(
0127             watcher,
0128             &QmlFileChangeWatcher::dirty,
0129             window,
0130             [window, url]() {
0131                 qDebug() << "Reloading";
0132                 qDebug() << "-------------------";
0133                 window->setSource(url);
0134             },
0135             Qt::QueuedConnection);
0136 
0137         QObject::connect(window, &PlasmaQuick::QuickViewSharedEngine::statusChanged, window, [window](QQmlComponent::Status status) {
0138             if (status == QQmlComponent::Error) {
0139                 qWarning() << "Error loading file";
0140             }
0141         });
0142         // status changed
0143     }
0144 
0145     return app.exec();
0146 }
0147 
0148 QmlFileChangeWatcher::QmlFileChangeWatcher(std::shared_ptr<QQmlEngine> engine, QObject *parent)
0149     : QObject(parent)
0150 {
0151     engine->addUrlInterceptor(this);
0152     m_resetTimer.setInterval(1000);
0153     m_resetTimer.setSingleShot(true);
0154     connect(&m_resetTimer, &QTimer::timeout, this, [this, engine]() {
0155         // reset watched files
0156         init();
0157         Q_EMIT dirty();
0158     });
0159     init();
0160 }
0161 
0162 void QmlFileChangeWatcher::init()
0163 {
0164     m_fileWatcher.reset(new KDirWatch(this));
0165     connect(m_fileWatcher.get(), &KDirWatch::dirty, this, [this]() {
0166         if (!m_resetTimer.isActive()) {
0167             m_resetTimer.start();
0168         }
0169     });
0170 }
0171 
0172 QUrl QmlFileChangeWatcher::intercept(const QUrl &path, DataType type)
0173 {
0174     switch (type) {
0175     case QmlFile:
0176     case JavaScriptFile:
0177     case QmldirFile:
0178         m_fileWatcher->addFile(path.toLocalFile());
0179         break;
0180     default:
0181         break;
0182     }
0183     return path;
0184 }
0185 
0186 #include "main.moc"