From 3afdafd8c237e18ddb15066b54f5b3d9827a5bd5 Mon Sep 17 00:00:00 2001 From: Devin Lin Date: Wed, 19 Mar 2025 15:10:08 -0400 Subject: [PATCH] folio: Use dynamic list insert/delete for changes Should help reduce the effect of https://invent.kde.org/plasma/plasma-mobile/-/issues/440 Instead of fully resetting the application list when the ksycoca db changes, calculate which items are removed and added based on the storageId and emit targeted signals for them. --- .../folio/applicationlistmodel.cpp | 77 ++++++++++++++----- .../homescreens/folio/applicationlistmodel.h | 4 + 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/containments/homescreens/folio/applicationlistmodel.cpp b/containments/homescreens/folio/applicationlistmodel.cpp index 89b0f25e..00415082 100644 --- a/containments/homescreens/folio/applicationlistmodel.cpp +++ b/containments/homescreens/folio/applicationlistmodel.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: 2014 Antonis Tsiapaliokas -// SPDX-FileCopyrightText: 2022 Devin Lin +// SPDX-FileCopyrightText: 2022-2024 Devin Lin // SPDX-License-Identifier: GPL-2.0-or-later #include "applicationlistmodel.h" @@ -53,13 +53,12 @@ void ApplicationListModel::sycocaDbChanged() load(); } -void ApplicationListModel::load() +KService::List ApplicationListModel::queryApplications() { auto cfg = KSharedConfig::openConfig(QStringLiteral("applications-blacklistrc")); auto blgroup = KConfigGroup(cfg, QStringLiteral("Applications")); const QStringList blacklist = blgroup.readEntry("blacklist", QStringList()); - auto filter = [blacklist](const KService::Ptr &service) -> bool { if (service->noDisplay()) { return false; @@ -76,27 +75,61 @@ void ApplicationListModel::load() return true; }; - beginResetModel(); + return KApplicationTrader::query(filter); +} - m_delegates.clear(); +void ApplicationListModel::load() +{ + qDebug() << "Reloading folio app list..."; - QList unorderedList; + // This function supports dynamic insertions and deletions to the existing + // list depending on what is given from queryApplications(). - const KService::List apps = KApplicationTrader::query(filter); - - for (const KService::Ptr &service : apps) { - FolioApplication::Ptr app = std::make_shared(m_homeScreen, service); - FolioDelegate::Ptr delegate = std::make_shared(app, m_homeScreen); - unorderedList << delegate; + QMap storageIdMap; // + for (int i = 0; i < m_delegates.size(); ++i) { + const auto &delegate = m_delegates[i]; + storageIdMap.insert(delegate->application()->storageId(), i); } - std::sort(unorderedList.begin(), unorderedList.end(), [](FolioDelegate::Ptr &a1, FolioDelegate::Ptr &a2) { - return a1->application()->name().compare(a2->application()->name(), Qt::CaseInsensitive) < 0; - }); + const KService::List currentApps = queryApplications(); + QList toInsert; - m_delegates << unorderedList; + for (const KService::Ptr &service : currentApps) { + auto it = storageIdMap.find(service->storageId()); + if (it != storageIdMap.end()) { + // Service already in m_delegates + storageIdMap.erase(it); + } else { + // Service needs to be inserted into m_delegates + toInsert.append(std::move(service)); + } + } - endResetModel(); + QList toRemove; + for (int index : storageIdMap.values()) { + toRemove.append(index); + } + + std::sort(toRemove.begin(), toRemove.end()); + + // Remove indices first, from end to start to avoid indicies changing + for (int i = toRemove.size() - 1; i >= 0; --i) { + int ind = toRemove[i]; + + beginRemoveRows({}, ind, ind); + m_delegates.removeAt(ind); + endRemoveRows(); + } + + // Append new elements + for (const KService::Ptr &service : toInsert) { + FolioApplication::Ptr app = std::make_shared(m_homeScreen, service); + FolioDelegate::Ptr delegate = std::make_shared(app, m_homeScreen); + + beginInsertRows({}, m_delegates.size(), m_delegates.size()); + m_delegates.append(delegate); + endInsertRows(); + } } QVariant ApplicationListModel::data(const QModelIndex &index, int role) const @@ -115,7 +148,7 @@ QVariant ApplicationListModel::data(const QModelIndex &index, int role) const if (!delegate->application()) { return QVariant(); } - return m_delegates.at(index.row())->application()->name(); + return delegate->application()->name(); default: return QVariant(); } @@ -133,7 +166,13 @@ int ApplicationListModel::rowCount(const QModelIndex &parent) const ApplicationListSearchModel::ApplicationListSearchModel(HomeScreen *parent, ApplicationListModel *model) : QSortFilterProxyModel(parent) { - setFilterCaseSensitivity(Qt::CaseInsensitive); setSourceModel(model); + setFilterRole(ApplicationListModel::NameRole); + setFilterCaseSensitivity(Qt::CaseInsensitive); + + setSortRole(ApplicationListModel::NameRole); + setSortCaseSensitivity(Qt::CaseInsensitive); + + sort(0, Qt::AscendingOrder); } diff --git a/containments/homescreens/folio/applicationlistmodel.h b/containments/homescreens/folio/applicationlistmodel.h index 0b3534bb..52f418dd 100644 --- a/containments/homescreens/folio/applicationlistmodel.h +++ b/containments/homescreens/folio/applicationlistmodel.h @@ -10,6 +10,8 @@ #include #include +#include + #include "foliodelegate.h" #include "homescreen.h" @@ -42,6 +44,8 @@ public Q_SLOTS: void sycocaDbChanged(); protected: + KService::List queryApplications(); + HomeScreen *m_homeScreen{nullptr}; QList> m_delegates;