diff --git a/src/qtquick/qml/DialogContent.qml b/src/qtquick/qml/DialogContent.qml index 00bd718f..bdf26c5f 100644 --- a/src/qtquick/qml/DialogContent.qml +++ b/src/qtquick/qml/DialogContent.qml @@ -1,78 +1,77 @@ /* * Copyright (C) 2019 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 . * */ /** * @brief The contents of the NewStuff.Dialog component * * This component is equivalent to the old DownloadWidget, but you should consider * using NewStuff.Page instead for a more modern style of integration into your * application's flow. * @see KNewStuff::DownloadWidget * @since 5.63 */ import QtQuick 2.11 import QtQuick.Layouts 1.11 as QtLayouts import org.kde.kirigami 2.7 as Kirigami import org.kde.newstuff 1.62 as NewStuff Kirigami.ApplicationItem { id: component property alias downloadNewWhat: newStuffPage.title /** * The configuration file to use for this button */ property alias configFile: newStuffPage.configFile /** * The engine which handles the content in this dialog */ property alias engine: newStuffPage.engine /** * The default view mode of the dialog spawned by this button. This should be * set using the NewStuff.Page.ViewMode enum * @see NewStuff.Page.ViewMode */ property alias viewMode: newStuffPage.viewMode QtLayouts.Layout.preferredWidth: Kirigami.Units.gridUnit * 50 QtLayouts.Layout.preferredHeight: Kirigami.Units.gridUnit * 40 pageStack.defaultColumnWidth: pageStack.width pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.Auto pageStack.initialPage: NewStuff.Page { id: newStuffPage function showMessage(message) { // As the Page shows something nice and friendly while loading, // there's no reason to do the passive notification thing for those. if (!engine.isLoading) { component.showPassiveNotification(message); } } - onMessage: showMessage(message); - onIdleMessage: showMessage(message); - onBusyMessage: showMessage(message); - onErrorMessage: showMessage(message); + onMessage: component.showPassiveNotification(message); + onIdleMessage: component.showPassiveNotification(message); + onErrorMessage: component.showPassiveNotification(message); } } diff --git a/src/qtquick/qml/Page.qml b/src/qtquick/qml/Page.qml index 7cb8076c..7d1df37f 100644 --- a/src/qtquick/qml/Page.qml +++ b/src/qtquick/qml/Page.qml @@ -1,273 +1,302 @@ /* * Copyright (C) 2019 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 . * */ /** * @brief A Kirigami.Page component used for managing KNS entries * * This component is functionally equivalent to the old DownloadDialog * @see KNewStuff::DownloadDialog * @since 5.63 */ import QtQuick 2.11 import QtQuick.Controls 2.11 as QtControls import QtQuick.Layouts 1.11 as QtLayouts import QtGraphicalEffects 1.11 as QtEffects import org.kde.kcm 1.2 as KCM import org.kde.kirigami 2.7 as Kirigami import org.kde.newstuff 1.62 as NewStuff import "private" as Private import "private/entrygriddelegates" as EntryGridDelegates KCM.GridViewKCM { id: root; /** * @brief The configuration file which describes the application (knsrc) * * The format and location of this file is found in the documentation for * KNS3::DownloadDialog */ property alias configFile: newStuffEngine.configFile; readonly property alias engine: newStuffEngine; /** * Any generic message from the NewStuff.Engine * @param message The message to be shown to the user */ signal message(string message); /** * A message posted usually describing that whatever action a recent busy * message said was happening has been completed * @param message The message to be shown to the user */ signal idleMessage(string message); /** * A message posted when the engine is busy doing something long duration * (usually this will be when fetching installation data) * @param message The message to be shown to the user */ signal busyMessage(string message); /** * A message posted when something has gone wrong * @param message The message to be shown to the user */ signal errorMessage(string message); property string uninstallLabel: i18nc("Request uninstallation of this item", "Uninstall"); property string useLabel: i18nc("If a knsrc file defines an adoption command, the option to run this command and 'use' an item becomes available. This is the text for an action to do so.", "Use"); property int viewMode: Page.ViewMode.Tiles enum ViewMode { Tiles, Icons, Preview } title: newStuffEngine.name NewStuff.Engine { id: newStuffEngine; property string statusMessage; onMessage: { root.message(message); statusMessage = message; } onIdleMessage: { root.idleMessage(message); statusMessage = message; } onBusyMessage: { root.busyMessage(message); statusMessage = message; } onErrorMessage: { root.errorMessage(message); statusMessage = message; } } NewStuff.QuestionAsker {} titleDelegate: QtLayouts.RowLayout { QtLayouts.Layout.fillWidth: true Kirigami.Heading { id: title level: 1 QtLayouts.Layout.fillWidth: true; opacity: root.isCurrentPage ? 1 : 0.4 maximumLineCount: 1 elide: Text.ElideRight text: root.title } QtControls.ButtonGroup { id: displayModeGroup buttons: [displayModeTiles, displayModeIcons] } QtControls.ToolButton { id: displayModeTiles icon.name: "view-list-details" onClicked: { root.viewMode = Page.ViewMode.Tiles; } checked: root.viewMode == Page.ViewMode.Tiles } QtControls.ToolButton { id: displayModeIcons icon.name: "view-list-icons" onClicked: { root.viewMode = Page.ViewMode.Icons; } checked: root.viewMode == Page.ViewMode.Icons } QtControls.ToolButton { id: displayPreview icon.name: "view-preview" onClicked: { root.viewMode = Page.ViewMode.Preview; } checked: root.viewMode == Page.ViewMode.Preview } Kirigami.ActionTextField { id: searchField placeholderText: i18n("Search...") focusSequence: "Ctrl+F" rightActions: [ Kirigami.Action { iconName: "edit-clear" visible: searchField.text !== "" onTriggered: { searchField.text = ""; searchField.accepted(); } } ] onAccepted: { newStuffEngine.searchTerm = searchField.text; } } } footer: QtLayouts.RowLayout { QtControls.ComboBox { id: categoriesCombo QtLayouts.Layout.fillWidth: true model: newStuffEngine.categories textRole: "displayName" onCurrentIndexChanged: { newStuffEngine.categoriesFilter = model.data(model.index(currentIndex, 0), NewStuff.CategoriesModel.NameRole); } } QtControls.ComboBox { id: filterCombo QtLayouts.Layout.fillWidth: true model: ListModel {} Component.onCompleted: { filterCombo.model.append({ text: i18nc("List option which will set the filter to show everything", "Show Everything") }); filterCombo.model.append({ text: i18nc("List option which will set the filter so only installed items are shown", "Installed Only") }); filterCombo.model.append({ text: i18nc("List option which will set the filter so only installed items with updates available are shown", "Updateable Only") }); filterCombo.currentIndex = newStuffEngine.filter; } onCurrentIndexChanged: { newStuffEngine.filter = currentIndex; } } QtControls.ComboBox { id: sortCombo QtLayouts.Layout.fillWidth: true model: ListModel { } Component.onCompleted: { sortCombo.model.append({ text: i18nc("List option which will set the sort order to based on when items were most recently updated", "Show most recent first") }); sortCombo.model.append({ text: i18nc("List option which will set the sort order to be alphabetical based on the name", "Sort alphabetically") }); sortCombo.model.append({ text: i18nc("List option which will set the sort order to based on user ratings", "Show highest rated first") }); sortCombo.model.append({ text: i18nc("List option which will set the sort order to based on number of downloads", "Show most downloaded first") }); sortCombo.currentIndex = newStuffEngine.sortOrder; } onCurrentIndexChanged: { newStuffEngine.sortOrder = currentIndex; } } } view.model: NewStuff.ItemsModel { id: newStuffModel; engine: newStuffEngine; } NewStuff.DownloadItemsSheet { id: downloadItemsSheet onItemPicked: { newStuffModel.installItem(entryId, downloadItemId); } } view.implicitCellWidth: root.viewMode == Page.ViewMode.Tiles ? Kirigami.Units.gridUnit * 30 : (root.viewMode == Page.ViewMode.Preview ? Kirigami.Units.gridUnit * 25 : Kirigami.Units.gridUnit * 10) view.implicitCellHeight: root.viewMode == Page.ViewMode.Tiles ? Math.round(view.implicitCellWidth / 3) : (root.viewMode == Page.ViewMode.Preview ? Kirigami.Units.gridUnit * 25 : Math.round(view.implicitCellWidth / 1.6) + Kirigami.Units.gridUnit*2) view.delegate: root.viewMode == Page.ViewMode.Tiles ? tileDelegate : (root.viewMode == Page.ViewMode.Preview ? bigPreviewDelegate : thumbDelegate) + view.footer: Item { + width: GridView.view.width + height: GridView.view.count > 0 ? Kirigami.Units.gridUnit * 3 : GridView.view.height + Behavior on height { NumberAnimation { duration: Kirigami.Units.shortDuration; } } + visible: opacity > 0 + opacity: newStuffModel.isLoadingData && !newStuffEngine.isLoading ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration; } } + QtControls.BusyIndicator { + anchors { + top: parent.top + right: parent.horizontalCenter + bottom: parent.bottom + margins: Kirigami.Units.smallSpacing + } + width: Kirigami.Units.gridUnit * 3 - Kirigami.Units.smallSpacing * 2 + running: parent.visible + QtControls.Label { + anchors { + top: parent.top + left: parent.right + leftMargin: Kirigami.Units.largeSpacing + bottom: parent.bottom + } + text: i18nc("A text shown beside a busy indicator suggesting that data is being fetched", "Loading more...") + verticalAlignment: Text.AlignVCenter + } + } + } + Component { id: bigPreviewDelegate EntryGridDelegates.BigPreviewDelegate { } } Component { id: tileDelegate EntryGridDelegates.TileDelegate { useLabel: root.useLabel uninstallLabel: root.uninstallLabel } } Component { id: thumbDelegate EntryGridDelegates.ThumbDelegate { useLabel: root.useLabel uninstallLabel: root.uninstallLabel } } Component { id: detailsPage; NewStuff.EntryDetails { } } Item { anchors.fill: parent opacity: newStuffEngine.isLoading ? 1 : 0 Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration; } } visible: opacity > 0 Rectangle { anchors.fill: parent color: Kirigami.Theme.backgroundColor opacity: 0.7 } QtControls.BusyIndicator { anchors { horizontalCenter: parent.horizontalCenter bottom: parent.verticalCenter bottomMargin: Kirigami.Units.largeSpacing } running: newStuffEngine.isLoading } QtControls.Label { anchors { top: parent.verticalCenter left: parent.left right: parent.right margins: Kirigami.Units.largeSpacing } horizontalAlignment: Text.AlignHCenter text: newStuffEngine.statusMessage } } } diff --git a/src/qtquick/quickitemsmodel.cpp b/src/qtquick/quickitemsmodel.cpp index 4551816f..fd4d164a 100644 --- a/src/qtquick/quickitemsmodel.cpp +++ b/src/qtquick/quickitemsmodel.cpp @@ -1,421 +1,431 @@ /* * Copyright (C) 2016 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 "quickitemsmodel.h" #include "quickengine.h" #include "knewstuffquick_debug.h" #include "itemsmodel.h" #include "engine.h" #include "downloadlinkinfo.h" #include "core/commentsmodel.h" #include #include #include class ItemsModel::Private { public: Private(ItemsModel *qq) : q(qq) , model(nullptr) , engine(nullptr) , coreEngine(nullptr) {} ~Private() { qDeleteAll(commentsModels); } ItemsModel *q; KNSCore::ItemsModel *model; Engine *engine; KNSCore::Engine *coreEngine; QHash commentsModels; + bool isLoadingData{false}; + bool initModel() { if (model) { return true; } if (!coreEngine) { return false; } model = new KNSCore::ItemsModel(coreEngine, q); + q->connect(coreEngine, &KNSCore::Engine::signalBusy, q, [=](){ isLoadingData = true; emit q->isLoadingDataChanged(); }); + q->connect(coreEngine, &KNSCore::Engine::signalIdle, q, [=](){ isLoadingData = false; emit q->isLoadingDataChanged(); }); + q->connect(coreEngine, &KNSCore::Engine::signalProvidersLoaded, coreEngine, &KNSCore::Engine::reloadEntries); // Entries have been fetched and should be shown: q->connect(coreEngine, &KNSCore::Engine::signalEntriesLoaded, model, &KNSCore::ItemsModel::slotEntriesLoaded); // An entry has changes - eg because it was installed q->connect(coreEngine, &KNSCore::Engine::signalEntryChanged, model, &KNSCore::ItemsModel::slotEntryChanged); q->connect(coreEngine, &KNSCore::Engine::signalEntryChanged, q, [=](const KNSCore::EntryInternal &entry){ emit q->entryChanged(model->row(entry)); }); q->connect(coreEngine, &KNSCore::Engine::signalResetView, model, &KNSCore::ItemsModel::clearEntries); q->connect(coreEngine, &KNSCore::Engine::signalEntryPreviewLoaded, model, &KNSCore::ItemsModel::slotEntryPreviewLoaded); q->connect(model, &KNSCore::ItemsModel::rowsInserted, q, &ItemsModel::rowsInserted); q->connect(model, &KNSCore::ItemsModel::rowsRemoved, q, &ItemsModel::rowsRemoved); q->connect(model, &KNSCore::ItemsModel::dataChanged, q, &ItemsModel::dataChanged); q->connect(model, &KNSCore::ItemsModel::modelReset, q, &ItemsModel::modelReset); return true; } }; ItemsModel::ItemsModel(QObject *parent) : QAbstractListModel(parent) , d(new Private(this)) { } ItemsModel::~ItemsModel() { delete d; } QHash ItemsModel::roleNames() const { static const QHash roles = QHash{ {Qt::DisplayRole, "display"}, {NameRole, "name"}, {UniqueIdRole, "uniqueId"}, {CategoryRole, "category"}, {HomepageRole, "homepage"}, {AuthorRole, "author"}, {LicenseRole, "license"}, {ShortSummaryRole, "shortSummary"}, {SummaryRole, "summary"}, {ChangelogRole, "changelog"}, {VersionRole, "version"}, {ReleaseDateRole, "releaseDate"}, {UpdateVersionRole, "updateVersion"}, {UpdateReleaseDateRole, "updateReleaseDate"}, {PayloadRole, "payload"}, {Qt::DecorationRole, "decoration"}, {PreviewsSmallRole, "previewsSmall"}, {PreviewsRole, "previews"}, {InstalledFilesRole, "installedFiles"}, {UnInstalledFilesRole, "uninstalledFiles"}, {RatingRole, "rating"}, {NumberOfCommentsRole, "numberOfComments"}, {DownloadCountRole, "downloadCount"}, {NumberFansRole, "numberFans"}, {NumberKnowledgebaseEntriesRole, "numberKnowledgebaseEntries"}, {KnowledgebaseLinkRole, "knowledgebaseLink"}, {DownloadLinksRole, "downloadLinks"}, {DonationLinkRole, "donationLink"}, {ProviderIdRole, "providerId"}, {SourceRole, "source"}, {StatusRole, "status"} }; return roles; } int ItemsModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; if (d->initModel()) return d->model->rowCount(QModelIndex()); return 0; } QVariant ItemsModel::data(const QModelIndex &index, int role) const { QVariant data; if (index.isValid() && d->initModel()) { KNSCore::EntryInternal entry = d->model->data(d->model->index(index.row()), Qt::UserRole).value(); switch (role) { case NameRole: case Qt::DisplayRole: data.setValue(entry.name()); break; case UniqueIdRole: data.setValue(entry.uniqueId()); break; case CategoryRole: data.setValue(entry.category()); break; case HomepageRole: data.setValue(entry.homepage()); break; case AuthorRole: { KNSCore::Author author = entry.author(); QVariantMap returnAuthor; returnAuthor[QStringLiteral("id")] = author.id(); returnAuthor[QStringLiteral("name")] = author.name(); returnAuthor[QStringLiteral("email")] = author.email(); returnAuthor[QStringLiteral("homepage")] = author.homepage(); returnAuthor[QStringLiteral("jabber")] = author.jabber(); returnAuthor[QStringLiteral("avatarUrl")] = author.avatarUrl(); returnAuthor[QStringLiteral("description")] = author.description(); data.setValue<>(returnAuthor); } break; case LicenseRole: data.setValue(entry.license()); break; case ShortSummaryRole: data.setValue(entry.shortSummary()); break; case SummaryRole: data.setValue(entry.summary()); break; case ChangelogRole: data.setValue(entry.changelog()); break; case VersionRole: data.setValue(entry.version()); break; case ReleaseDateRole: data.setValue(entry.releaseDate()); break; case UpdateVersionRole: data.setValue(entry.updateVersion()); break; case UpdateReleaseDateRole: data.setValue(entry.updateReleaseDate()); break; case PayloadRole: data.setValue(entry.payload()); break; case Qt::DecorationRole: data.setValue(entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1)); break; case PreviewsSmallRole: { QStringList previews; previews << entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1); previews << entry.previewUrl(KNSCore::EntryInternal::PreviewSmall2); previews << entry.previewUrl(KNSCore::EntryInternal::PreviewSmall3); while(!previews.isEmpty() && previews.last().isEmpty()) { previews.takeLast(); } data.setValue(previews); } break; case PreviewsRole: { QStringList previews; previews << entry.previewUrl(KNSCore::EntryInternal::PreviewBig1); previews << entry.previewUrl(KNSCore::EntryInternal::PreviewBig2); previews << entry.previewUrl(KNSCore::EntryInternal::PreviewBig3); while(!previews.isEmpty() && previews.last().isEmpty()) { previews.takeLast(); } data.setValue(previews); } break; case InstalledFilesRole: data.setValue(entry.installedFiles()); break; case UnInstalledFilesRole: data.setValue(entry.uninstalledFiles()); break; case RatingRole: data.setValue(entry.rating()); break; case NumberOfCommentsRole: data.setValue(entry.numberOfComments()); break; case DownloadCountRole: data.setValue(entry.downloadCount()); break; case NumberFansRole: data.setValue(entry.numberFans()); break; case NumberKnowledgebaseEntriesRole: data.setValue(entry.numberKnowledgebaseEntries()); break; case KnowledgebaseLinkRole: data.setValue(entry.knowledgebaseLink()); break; case DownloadLinksRole: { // This would be good to cache... but it also needs marking as dirty, somehow... const QList dllinks = entry.downloadLinkInformationList(); QObjectList list; for(const KNSCore::EntryInternal::DownloadLinkInformation &link : dllinks) { DownloadLinkInfo *info = new DownloadLinkInfo(); info->setData(link); list.append(info); } data.setValue(list); } break; case DonationLinkRole: data.setValue(entry.donationLink()); break; case ProviderIdRole: data.setValue(entry.providerId()); break; case SourceRole: { KNSCore::EntryInternal::Source src = entry.source(); switch(src) { case KNSCore::EntryInternal::Cache: data.setValue(QStringLiteral("Cache")); break; case KNSCore::EntryInternal::Online: data.setValue(QStringLiteral("Online")); break; case KNSCore::EntryInternal::Registry: data.setValue(QStringLiteral("Registry")); break; default: data.setValue(QStringLiteral("Unknown source - shouldn't be possible")); break; } } break; case StatusRole: { KNS3::Entry::Status status = entry.status(); switch(status) { case KNS3::Entry::Downloadable: data.setValue(ItemsModel::DownloadableStatus); break; case KNS3::Entry::Installed: data.setValue(ItemsModel::InstalledStatus); break; case KNS3::Entry::Updateable: data.setValue(ItemsModel::UpdateableStatus); break; case KNS3::Entry::Deleted: data.setValue(ItemsModel::DeletedStatus); break; case KNS3::Entry::Installing: data.setValue(ItemsModel::InstallingStatus); break; case KNS3::Entry::Updating: data.setValue(ItemsModel::UpdatingStatus); break; case KNS3::Entry::Invalid: default: data.setValue(ItemsModel::InvalidStatus); break; } } break; case CommentsModelRole: { KNSCore::CommentsModel *commentsModel{nullptr}; if (!d->commentsModels.contains(entry.uniqueId())) { commentsModel = d->coreEngine->commentsForEntry(entry); d->commentsModels[entry.uniqueId()] = commentsModel; } else { commentsModel = d->commentsModels[entry.uniqueId()]; } data.setValue(commentsModel); } break; default: data.setValue(QStringLiteral("Unknown role")); break; } } return data; } bool ItemsModel::canFetchMore(const QModelIndex &parent) const { if (!parent.isValid() && d->coreEngine && d->coreEngine->categoriesMetadata().count() > 0) { return true; } return false; } void ItemsModel::fetchMore(const QModelIndex &parent) { if (parent.isValid() || !d->coreEngine) { return; } d->coreEngine->requestMoreData(); } QObject *ItemsModel::engine() const { return d->engine; } void ItemsModel::setEngine(QObject *newEngine) { if (d->engine != newEngine) { beginResetModel(); d->engine = qobject_cast(newEngine); d->model->deleteLater(); d->model = nullptr; d->coreEngine = nullptr; if (d->engine) { d->coreEngine = qobject_cast(d->engine->engine()); } connect(d->engine, &Engine::engineChanged, this, [this](){ beginResetModel(); d->model->deleteLater(); d->model = nullptr; d->coreEngine = qobject_cast(d->engine->engine()); endResetModel(); }); emit engineChanged(); endResetModel(); } } +bool ItemsModel::isLoadingData() const +{ + return d->isLoadingData; +} + void ItemsModel::installItem(int index, int linkId) { if (d->coreEngine) { KNSCore::EntryInternal entry = d->model->data(d->model->index(index), Qt::UserRole).value(); if(entry.isValid()) { d->coreEngine->install(entry, linkId); } } } void ItemsModel::uninstallItem(int index) { if (d->coreEngine) { KNSCore::EntryInternal entry = d->model->data(d->model->index(index), Qt::UserRole).value(); if(entry.isValid()) { d->coreEngine->uninstall(entry); } } } void ItemsModel::adoptItem(int index) { if (d->coreEngine) { KNSCore::EntryInternal entry = d->model->data(d->model->index(index), Qt::UserRole).value(); if (entry.isValid()) { QStringList args = KShell::splitArgs(d->coreEngine->adoptionCommand(entry)); qCDebug(KNEWSTUFFQUICK) << "executing AdoptionCommand" << args; QProcess::startDetached(args.takeFirst(), args); d->engine->idleMessage(i18n("Using %1").arg(entry.name())); } } } diff --git a/src/qtquick/quickitemsmodel.h b/src/qtquick/quickitemsmodel.h index 8f6d4647..4aba37dc 100644 --- a/src/qtquick/quickitemsmodel.h +++ b/src/qtquick/quickitemsmodel.h @@ -1,172 +1,188 @@ /* * Copyright (C) 2016 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 ITEMSMODEL_H #define ITEMSMODEL_H #include /** * @short A model which shows the contents found in an Engine * * Use an instance of this model to show the content items represented by the configuration * file passed to an engine. The following sample assumes you are using the Engine component, * however it is also possible to pass a KNSCore::Engine instance created from C++ to this * property, if you have specific requirements not covered by the convenience component. * * Most data in the model is simple, but the DownloadLinks role will return a list of * DownloadLinkInfo entries, which you will need to manage in some way. * * You might also look at NewStuffList, NewStuffItem, and the other items, to see some more * detail on what can be done with the data. * * @see NewStuffList * @see NewStuffItem * @see NewStuffPage * @see NewStuffEntryDetails * @see NewStuffEntryComments * * \code import org.kde.newstuff 1.0 as NewStuff Item { NewStuff.ItemsModel { id: newStuffModel; engine: newStuffEngine.engine; } NewStuff.Engine { id: newStuffEngine; configFile: "/some/filesystem/location/wallpaper.knsrc"; onMessage: console.log("KNS Message: " + message); onIdleMessage: console.log("KNS Idle: " + message); onBusyMessage: console.log("KNS Busy: " + message); onErrorMessage: console.log("KNS Error: " + message); } } \endcode */ class ItemsModel : public QAbstractListModel { Q_OBJECT /** * The NewStuffQuickEngine to show items from */ Q_PROPERTY(QObject* engine READ engine WRITE setEngine NOTIFY engineChanged) + /** + * Whether or not the model is fetching information from a remote location + * @since 5.64 + */ + Q_PROPERTY(bool isLoadingData READ isLoadingData NOTIFY isLoadingDataChanged) public: explicit ItemsModel(QObject *parent = nullptr); virtual ~ItemsModel(); enum Roles { NameRole = Qt::UserRole + 1, UniqueIdRole, CategoryRole, HomepageRole, AuthorRole, LicenseRole, ShortSummaryRole, SummaryRole, ChangelogRole, VersionRole, ReleaseDateRole, UpdateVersionRole, UpdateReleaseDateRole, PayloadRole, PreviewsSmallRole, ///@< this will return a list here, rather than be tied so tightly to the remote api PreviewsRole, ///@< this will return a list here, rather than be tied so tightly to the remote api InstalledFilesRole, UnInstalledFilesRole, RatingRole, NumberOfCommentsRole, DownloadCountRole, NumberFansRole, NumberKnowledgebaseEntriesRole, KnowledgebaseLinkRole, DownloadLinksRole, DonationLinkRole, ProviderIdRole, SourceRole, StatusRole, CommentsModelRole }; Q_ENUM(Roles) enum ItemStatus { InvalidStatus, DownloadableStatus, InstalledStatus, UpdateableStatus, DeletedStatus, InstallingStatus, UpdatingStatus }; Q_ENUM(ItemStatus) QHash< int, QByteArray > roleNames() const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; bool canFetchMore(const QModelIndex & parent) const override; void fetchMore(const QModelIndex & parent) override; QObject *engine() const; void setEngine(QObject *newEngine); Q_SIGNAL void engineChanged(); + /** + * Whether or not the model is fetching information from a remote location + * @since 5.64 + */ + bool isLoadingData() const; + /** + * Fired when the isLoadingData value changes + * @since 5.64 + */ + Q_SIGNAL void isLoadingDataChanged(); + /** * @brief This will install (or update, if already installed) the item at the given index * * There are no side effects of this function if it is called on an item which cannot be * installed or updated (that is, if the status is not one such that these are possible, * the function will simply return without performing any actions) * * @param index The index of the item to install or update */ Q_INVOKABLE void installItem(int index, int linkId); /** * @brief Uninstall an already installed item * * There are no side effects of this function if it is called on an item which cannot be * uninstalled (that is, if the status is not one such that this is possible, * the function will simply return without performing any actions) * * @param index The index of the item to be uninstalled */ Q_INVOKABLE void uninstallItem(int index); /** * @brief Run the adoption command on an already installed item * * @note This will simply fail quietly if the item is not installed * * @param index The intex of the item to be adopted */ Q_INVOKABLE void adoptItem(int index); /** * @brief Fired when an entry's data changes * * @param index The index of the item which has changed */ Q_SIGNAL void entryChanged(int index); private: class Private; Private *d; }; Q_DECLARE_METATYPE(ItemsModel::ItemStatus) #endif//ITEMSMODEL_H