diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -5,6 +5,7 @@ downloadmanager.cpp engine.cpp entryinternal.cpp + entrywrapper.cpp imageloader.cpp installation.cpp itemsmodel.cpp @@ -82,6 +83,7 @@ DownloadManager Engine EntryInternal + EntryWrapper ErrorCode Installation ItemsModel diff --git a/src/core/entrywrapper.h b/src/core/entrywrapper.h new file mode 100644 --- /dev/null +++ b/src/core/entrywrapper.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 Dan Leinir Turthra Jensen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) version 3, or any + * later version accepted by the membership of KDE e.V. (or its + * successor approved by the membership of KDE e.V.), which shall + * act as a proxy defined in Section 6 of version 3 of the license. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#ifndef ENTRYWRAPPER_H +#define ENTRYWRAPPER_H + +#include + +#include "entryinternal.h" +#include "knewstuffcore_export.h" + +namespace KNSCore +{ +// This is for passing an entryinternal through qml, particularly useful for lists +// such as changedEntries. This is supposed to closely approximate the current +// codepaths used in client code (which expects a list of entries), but for KF6 +// we will want to turn this into a model instead, probably with some handy +// iteration assistance for use in places which would previously use the lists. +/// TODO KF6 see above (in short, make this class irrelevant so it can be removed) + +/** + * @short Wraps a KNSCore::EntryInternal in a QObject for passing through Qt Quick + * + * For those places where we need to pass a KNSCore::EntryInternal through Qt Quick, + * this class wraps such objects in a QObject, and provides the ability to interact + * with the data through that. + * + * Since that class is not a QObject (and can't easily be turned into one), until major + * upheaval becomes possible in Frameworks 6, this class will have to do. + * @since 5.67 + */ +class KNEWSTUFFCORE_EXPORT EntryWrapper : public QObject +{ + Q_OBJECT +public: + explicit EntryWrapper(const EntryInternal& entry, QObject *parent = nullptr); + virtual ~EntryWrapper(); + + /** + * Get a copy of the wrapped-up entry + * @return A copy of the entry + */ + EntryInternal entry() const; +private: + class Private; + Private *d; +}; +} +Q_DECLARE_METATYPE(KNSCore::EntryWrapper*) + +#endif//ENTRYWRAPPER_H diff --git a/src/core/entrywrapper.cpp b/src/core/entrywrapper.cpp new file mode 100644 --- /dev/null +++ b/src/core/entrywrapper.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 Dan Leinir Turthra Jensen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) version 3, or any + * later version accepted by the membership of KDE e.V. (or its + * successor approved by the membership of KDE e.V.), which shall + * act as a proxy defined in Section 6 of version 3 of the license. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + */ + +#include "entrywrapper.h" + +namespace KNSCore { + class EntryWrapper::Private { + public: + Private(const EntryInternal& entry + ) + : entry(entry) + {} + EntryInternal entry; + }; +} + +KNSCore::EntryWrapper::EntryWrapper(const KNSCore::EntryInternal& entry, QObject* parent) + : QObject(parent) + , d(new Private(entry)) +{} + +KNSCore::EntryWrapper::~EntryWrapper() +{ + delete d; +} + +KNSCore::EntryInternal KNSCore::EntryWrapper::entry() const +{ + return d->entry; +} diff --git a/src/qtquick/qml/Button.qml b/src/qtquick/qml/Button.qml --- a/src/qtquick/qml/Button.qml +++ b/src/qtquick/qml/Button.qml @@ -82,7 +82,12 @@ * changed since the dialog was opened most recently (rather than the lifetime * of the instance of the Button component) */ - property alias changedEntries: ghnsDialog.changedEntries + property var changedEntries + Binding { + target: component + property: "changedEntries" + value: ghnsDialog.engine.changedEntries + } /** * If this is true (default is false), the button will be shown when the Kiosk settings are such diff --git a/src/qtquick/qmlplugin.cpp b/src/qtquick/qmlplugin.cpp --- a/src/qtquick/qmlplugin.cpp +++ b/src/qtquick/qmlplugin.cpp @@ -28,6 +28,7 @@ #include "categoriesmodel.h" #include "commentsmodel.h" #include "downloadlinkinfo.h" +#include "entrywrapper.h" #include "provider.h" #include "question.h" @@ -42,17 +43,25 @@ void QmlPlugins::registerTypes(const char *uri) { + const char* coreUri{"org.kde.newstuff.core"}; + + // Initial version qmlRegisterType(uri, 1, 0, "Engine"); qmlRegisterType(uri, 1, 0, "ItemsModel"); + + // Version 1.62 qmlRegisterType(uri, 1, 62, "Author"); qmlRegisterType(uri, 1, 62, "CommentsModel"); qmlRegisterUncreatableType(uri, 1, 0, "DownloadLinkInfo", QStringLiteral("This should only be created by the ItemsModel, and is associated with one entry in that model")); qmlRegisterUncreatableType(uri, 1, 0, "CategoriesModel", QStringLiteral("This should only be created by the Engine, and provides the categories available in that engine")); - qmlRegisterUncreatableMetaObject(KNSCore::Provider::staticMetaObject, "org.kde.newstuff.core", 1, 62, "Provider", QLatin1String("Error: this only exists to forward enums")); - qmlRegisterUncreatableMetaObject(KNSCore::Question::staticMetaObject, "org.kde.newstuff.core", 1, 62, "Question", QLatin1String("Error: this only exists to forward enums")); + qmlRegisterUncreatableMetaObject(KNSCore::Provider::staticMetaObject, coreUri, 1, 62, "Provider", QLatin1String("Error: this only exists to forward enums")); + qmlRegisterUncreatableMetaObject(KNSCore::Question::staticMetaObject, coreUri, 1, 62, "Question", QLatin1String("Error: this only exists to forward enums")); qmlRegisterSingletonType(uri, 1, 62, "QuickQuestionListener", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { Q_UNUSED(scriptEngine) engine->setObjectOwnership(KNewStuffQuick::QuickQuestionListener::instance(), QQmlEngine::CppOwnership); return KNewStuffQuick::QuickQuestionListener::instance(); }); + + // Version 1.67 + qmlRegisterUncreatableType(coreUri, 1, 67, "EntryWrapper", QStringLiteral("This should only be created by the Engine, and wraps EntryInternal objects for passing through Qt Quick")); } diff --git a/src/qtquick/quickengine.h b/src/qtquick/quickengine.h --- a/src/qtquick/quickengine.h +++ b/src/qtquick/quickengine.h @@ -23,7 +23,9 @@ #define ENGINE_H #include -#include +#include + +#include "entrywrapper.h" /** * @short Encapsulates a KNSCore::Engine for use in Qt Quick @@ -51,7 +53,7 @@ Q_PROPERTY(int filter READ filter WRITE setFilter NOTIFY filterChanged) Q_PROPERTY(int sortOrder READ sortOrder WRITE setSortOrder NOTIFY sortOrderChanged) Q_PROPERTY(QString searchTerm READ searchTerm WRITE setSearchTerm RESET resetSearchTerm NOTIFY searchTermChanged) - Q_PROPERTY(KNSCore::EntryInternal::List changedEntries READ changedEntries RESET resetChangedEntries NOTIFY changedEntriesChanged) + Q_PROPERTY(QQmlListProperty changedEntries READ changedEntries NOTIFY changedEntriesChanged) Q_PROPERTY(int changedEntriesCount READ changedEntriesCount NOTIFY changedEntriesChanged) public: explicit Engine(QObject *parent = nullptr); @@ -102,7 +104,7 @@ Q_INVOKABLE void resetSearchTerm(); Q_SIGNAL void searchTermChanged(); - KNSCore::EntryInternal::List changedEntries() const; + QQmlListProperty changedEntries(); Q_INVOKABLE void resetChangedEntries(); Q_SIGNAL void changedEntriesChanged(); int changedEntriesCount() const; diff --git a/src/qtquick/quickengine.cpp b/src/qtquick/quickengine.cpp --- a/src/qtquick/quickengine.cpp +++ b/src/qtquick/quickengine.cpp @@ -25,6 +25,7 @@ #include #include "categoriesmodel.h" +#include "entrywrapper.h" #include "quickquestionlistener.h" #include "engine.h" @@ -40,7 +41,34 @@ bool isLoading{false}; CategoriesModel *categoriesModel; QString configFile; + KNSCore::EntryInternal::List changedEntries; + static KNSCore::EntryWrapper *getChangedEntry(QQmlListProperty* property, int i) + { + KNSCore::EntryWrapper *entry{nullptr}; + if (property) { + Private* d = static_cast(property->data); + if (d) { + if (i >= 0 && i < d->changedEntries.count()) { + // Lifetime management for these objects should be done by the consumer, + // but are also parented for auto-delete on application shutdown + entry = new KNSCore::EntryWrapper(d->changedEntries[i], property->object); + } + } + } + return entry; + } + static int getChangedEntriesCount(QQmlListProperty* property) + { + int count{0}; + if (property) { + Private* d = static_cast(property->data); + if (d) { + count = d->changedEntries.count(); + } + } + return count; + } }; Engine::Engine(QObject *parent) @@ -94,6 +122,9 @@ emit errorMessage(message); }); connect(d->engine, &KNSCore::Engine::signalEntryChanged, this, [this](const KNSCore::EntryInternal &entry){ + if (d->changedEntries.contains(entry) ) { + d->changedEntries.removeAll(entry); + } d->changedEntries << entry; emit changedEntriesChanged(); }); @@ -226,9 +257,9 @@ setSearchTerm(QString{}); } -KNSCore::EntryInternal::List Engine::changedEntries() const +QQmlListProperty Engine::changedEntries() { - return d->changedEntries; + return QQmlListProperty(this, d, &Private::getChangedEntriesCount, &Private::getChangedEntry); } int Engine::changedEntriesCount() const