File indexing completed on 2024-04-21 15:02:29

0001 /*
0002     SPDX-FileCopyrightText: 2020 Alexander Lohnau <alexander.lohnau@gmx.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include "qtquickdialogwrapper.h"
0008 
0009 #include <QEventLoop>
0010 #include <QGuiApplication>
0011 #include <QQmlComponent>
0012 #include <QQmlContext>
0013 #include <QQmlEngine>
0014 #include <QTimer>
0015 #include <QWindow>
0016 
0017 #include <KLocalizedContext>
0018 
0019 #include "core/engine.h"
0020 #include "knewstuff_debug.h"
0021 
0022 using namespace KNS3;
0023 
0024 class KNS3::QtQuickDialogWrapperPrivate
0025 {
0026 public:
0027     QQmlEngine *engine = nullptr;
0028     QObject *item = nullptr;
0029     KNSCore::Engine *coreEngine = nullptr;
0030     QList<KNSCore::EntryInternal> changedEntries;
0031 };
0032 
0033 QtQuickDialogWrapper::QtQuickDialogWrapper(const QString &configFile, QObject *parent)
0034     : QObject(parent)
0035     , d(new QtQuickDialogWrapperPrivate())
0036 {
0037     d->engine = new QQmlEngine(this);
0038     auto *context = new KLocalizedContext(d->engine);
0039     context->setTranslationDomain(QStringLiteral("knewstuff5"));
0040     d->engine->rootContext()->setContextObject(context);
0041     QQmlComponent component(d->engine);
0042     d->engine->rootContext()->setContextProperty(QStringLiteral("knsrcfile"), configFile);
0043     component.setData(QByteArrayLiteral("import QtQuick 2.7\n"
0044                                         "import org.kde.newstuff 1.62 as NewStuff\n"
0045                                         "\n"
0046                                         "NewStuff.Dialog {\n"
0047                                         "    id: component\n"
0048                                         "    signal closed()\n"
0049                                         "    configFile: knsrcfile\n"
0050                                         "    onVisibleChanged: if (!visible) {closed()}\n"
0051                                         "}"),
0052                       QUrl());
0053     d->item = component.create();
0054     // If there is an error on the QML side of things we get a nullptr
0055     if (d->item) {
0056         d->item->setParent(this);
0057 
0058         QObject *qtquickEngine = d->item->property("engine").value<QObject *>();
0059         Q_ASSERT(qtquickEngine);
0060         d->coreEngine = qtquickEngine->property("engine").value<KNSCore::Engine *>();
0061         Q_ASSERT(d->coreEngine);
0062 
0063         connect(d->coreEngine, &KNSCore::Engine::signalEntryEvent, this, [this](const KNSCore::EntryInternal &entry, KNSCore::EntryInternal::EntryEvent event) {
0064             if (event == KNSCore::EntryInternal::StatusChangedEvent) {
0065                 if (entry.status() == KNS3::Entry::Installing || entry.status() == KNS3::Entry::Updating) {
0066                     return; // We do not care about intermediate states
0067                 }
0068                 // To make sure we have no duplicates and always the latest entry
0069                 d->changedEntries.removeOne(entry);
0070                 d->changedEntries.append(entry);
0071             }
0072         });
0073 
0074         // Forward relevant signals
0075         connect(d->item, SIGNAL(closed()), this, SIGNAL(closed()));
0076 
0077         // Otherwise, the dialog is not in front of other popups, BUG: 452593
0078         auto window = qobject_cast<QWindow *>(d->item);
0079         Q_ASSERT(window);
0080         auto transientParent = QGuiApplication::focusWindow();
0081 
0082         // TODO KF6: only use focusWindow as transientParent
0083         // BUG 454895: If transientParent is a menu/popup, don't use it as a parent
0084         // Instead follow its parent until we get a "real" window
0085         while (transientParent && transientParent->flags().testFlag(Qt::Popup)) {
0086             transientParent = transientParent->transientParent();
0087         }
0088 
0089         window->setTransientParent(transientParent);
0090     } else {
0091         qWarning(KNEWSTUFF) << "Error creating QtQuickDialogWrapper component:" << component.errors();
0092     }
0093 }
0094 
0095 QtQuickDialogWrapper::~QtQuickDialogWrapper()
0096 {
0097     // Empty destructor needed for std::unique_ptr to incomplete class.
0098 }
0099 
0100 void QtQuickDialogWrapper::open()
0101 {
0102     if (d->item) {
0103         d->changedEntries.clear();
0104         QMetaObject::invokeMethod(d->item, "open");
0105     }
0106 }
0107 
0108 KNSCore::Engine *QtQuickDialogWrapper::engine()
0109 {
0110     return d->coreEngine;
0111 }
0112 
0113 QList<KNSCore::Entry> QtQuickDialogWrapper::changedEntries() const
0114 {
0115     return d->changedEntries;
0116 }
0117 
0118 #if KNEWSTUFF_BUILD_DEPRECATED_SINCE(5, 94)
0119 QList<KNSCore::EntryInternal> QtQuickDialogWrapper::exec()
0120 {
0121     open();
0122     QEventLoop loop;
0123     connect(this, &QtQuickDialogWrapper::closed, &loop, &QEventLoop::quit);
0124     loop.exec();
0125     return d->changedEntries;
0126 }
0127 #endif
0128 
0129 #include "moc_qtquickdialogwrapper.cpp"