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.
This commit is contained in:
Devin Lin 2025-03-19 15:10:08 -04:00
parent 5d443aa679
commit 3afdafd8c2
2 changed files with 62 additions and 19 deletions

View file

@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: 2014 Antonis Tsiapaliokas <antonis.tsiapaliokas@kde.org>
// SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
// SPDX-FileCopyrightText: 2022-2024 Devin Lin <devin@kde.org>
// 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<FolioDelegate::Ptr> 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<FolioApplication>(m_homeScreen, service);
FolioDelegate::Ptr delegate = std::make_shared<FolioDelegate>(app, m_homeScreen);
unorderedList << delegate;
QMap<QString, int> storageIdMap; // <storageId, index>
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<KService::Ptr> 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<int> 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<FolioApplication>(m_homeScreen, service);
FolioDelegate::Ptr delegate = std::make_shared<FolioDelegate>(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);
}

View file

@ -10,6 +10,8 @@
#include <QQuickItem>
#include <QSet>
#include <KService>
#include "foliodelegate.h"
#include "homescreen.h"
@ -42,6 +44,8 @@ public Q_SLOTS:
void sycocaDbChanged();
protected:
KService::List queryApplications();
HomeScreen *m_homeScreen{nullptr};
QList<std::shared_ptr<FolioDelegate>> m_delegates;