Save sorted list

pull/9/head
reionwong 4 years ago
parent 5abb8268ee
commit 046f912d5a

@ -19,7 +19,7 @@ set(SRCS
src/iconthemeimageprovider.cpp src/iconthemeimageprovider.cpp
src/launcher.cpp src/launcher.cpp
src/launchermodel.cpp src/launchermodel.cpp
src/launcheritem.cpp src/appitem.cpp
src/main.cpp src/main.cpp
src/pagemodel.cpp src/pagemodel.cpp
src/ucunits.cpp src/ucunits.cpp

@ -1,7 +1,7 @@
/* /*
* Copyright (C) 2021 CutefishOS. * Copyright (C) 2021 CutefishOS.
* *
* Author: revenmartin <revenmartin@gmail.com> * Author: Reion Wong <reion@cutefishos.com>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -17,33 +17,41 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "launcheritem.h" #include "appitem.h"
LauncherItem::LauncherItem() AppItem::AppItem()
{ {
} }
LauncherItem::LauncherItem(const LauncherItem &item) AppItem::AppItem(const AppItem &info)
: id(info.id)
, name(info.name)
, genericName(info.genericName)
, comment(info.comment)
, iconName(info.iconName)
, args(info.args)
{ {
} }
LauncherItem::~LauncherItem() AppItem::~AppItem()
{ {
} }
QDataStream &operator<<(QDataStream &argument, const LauncherItem &item) QDataStream &operator<<(QDataStream &argument, const AppItem &info)
{ {
argument << item.id << item.name; argument << info.id << info.name << info.genericName;
argument << info.comment << info.iconName;
return argument; return argument;
} }
const QDataStream &operator>>(QDataStream &argument, LauncherItem &item) const QDataStream &operator>>(QDataStream &argument, AppItem &info)
{ {
argument >> item.id << item.name; argument >> info.id >> info.name >> info.genericName;
argument >> info.comment >> info.iconName;
return argument; return argument;
} }

@ -1,7 +1,7 @@
/* /*
* Copyright (C) 2021 CutefishOS. * Copyright (C) 2021 CutefishOS.
* *
* Author: revenmartin <revenmartin@gmail.com> * Author: Reion Wong <reion@cutefishos.com>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -17,26 +17,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef LAUNCHERITEM_H #ifndef APPITEM_H
#define LAUNCHERITEM_H #define APPITEM_H
#include <QObject> #include <QString>
#include <QDataStream> #include <QStringList>
#include <QMetaType>
class LauncherItem : public QObject class AppItem
{ {
Q_OBJECT
public: public:
LauncherItem(); AppItem();
LauncherItem(const LauncherItem &item); AppItem(const AppItem &info);
~LauncherItem(); ~AppItem();
inline bool operator==(const LauncherItem &other) const { return id == other.id; } inline bool operator==(const AppItem &other) const { return id == other.id; }
friend QDataStream &operator<<(QDataStream &argument, const LauncherItem &item); friend QDataStream &operator<<(QDataStream &argument, const AppItem &info);
friend const QDataStream &operator>>(QDataStream &argument, LauncherItem &item); friend const QDataStream &operator>>(QDataStream &argument, AppItem &info);
public:
QString id; QString id;
QString name; QString name;
QString genericName; QString genericName;
@ -45,6 +43,6 @@ public:
QStringList args; QStringList args;
}; };
Q_DECLARE_METATYPE(LauncherItem) Q_DECLARE_METATYPE(AppItem)
#endif // LAUNCHERITEM_H #endif // APPITEM_H

@ -1,7 +1,7 @@
/* /*
* Copyright (C) 2021 CutefishOS. * Copyright (C) 2021 CutefishOS.
* *
* Author: revenmartin <revenmartin@gmail.com> * Author: Reion Wong <reion@cutefishos.com>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -47,7 +47,16 @@ LauncherModel::LauncherModel(QObject *parent)
: QAbstractListModel(parent) : QAbstractListModel(parent)
, m_settings("cutefishos", "launcher-applist", this) , m_settings("cutefishos", "launcher-applist", this)
, m_mode(NormalMode) , m_mode(NormalMode)
, m_needSort(false)
{ {
// Init datas.
QByteArray listByteArray = m_settings.value("list").toByteArray();
QDataStream in(&listByteArray, QIODevice::ReadOnly);
in >> m_appItems;
if (m_appItems.isEmpty())
m_needSort = true;
QtConcurrent::run(LauncherModel::refresh, this); QtConcurrent::run(LauncherModel::refresh, this);
QFileSystemWatcher *watcher = new QFileSystemWatcher(this); QFileSystemWatcher *watcher = new QFileSystemWatcher(this);
@ -56,24 +65,18 @@ LauncherModel::LauncherModel(QObject *parent)
QtConcurrent::run(LauncherModel::refresh, this); QtConcurrent::run(LauncherModel::refresh, this);
}); });
m_saveTimer.setInterval(1000);
connect(&m_saveTimer, &QTimer::timeout, this, &LauncherModel::save);
connect(this, &QAbstractItemModel::rowsInserted, this, &LauncherModel::countChanged); connect(this, &QAbstractItemModel::rowsInserted, this, &LauncherModel::countChanged);
connect(this, &QAbstractItemModel::rowsRemoved, this, &LauncherModel::countChanged); connect(this, &QAbstractItemModel::rowsRemoved, this, &LauncherModel::countChanged);
connect(this, &QAbstractItemModel::modelReset, this, &LauncherModel::countChanged); connect(this, &QAbstractItemModel::modelReset, this, &LauncherModel::countChanged);
connect(this, &QAbstractItemModel::layoutChanged, this, &LauncherModel::countChanged); connect(this, &QAbstractItemModel::layoutChanged, this, &LauncherModel::countChanged);
connect(this, &LauncherModel::refreshed, this, &LauncherModel::onRefreshed);
connect(this, &LauncherModel::refreshed, this, [=] {
beginResetModel();
std::sort(m_items.begin(), m_items.end(), [=] (LauncherItem *a, LauncherItem *b) {
return a->name < b->name;
});
endResetModel();
});
} }
LauncherModel::~LauncherModel() LauncherModel::~LauncherModel()
{ {
while (!m_items.isEmpty())
delete m_items.takeFirst();
} }
int LauncherModel::count() const int LauncherModel::count() const
@ -88,7 +91,7 @@ int LauncherModel::rowCount(const QModelIndex &parent) const
if (m_mode == SearchMode) if (m_mode == SearchMode)
return m_searchItems.size(); return m_searchItems.size();
return m_items.size(); return m_appItems.size();
} }
QHash<int, QByteArray> LauncherModel::roleNames() const QHash<int, QByteArray> LauncherModel::roleNames() const
@ -112,21 +115,21 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
if (!index.isValid()) if (!index.isValid())
return QVariant(); return QVariant();
LauncherItem *item = m_mode == NormalMode AppItem appItem = m_mode == NormalMode ? m_appItems.at(index.row())
? m_items.at(index.row()) : m_searchItems.at(index.row()); : m_searchItems.at(index.row());
switch (role) { switch (role) {
case AppIdRole: case AppIdRole:
return item->id; return appItem.id;
case NameRole: case NameRole:
return item->name; return appItem.name;
case IconNameRole: case IconNameRole:
return item->iconName; return appItem.iconName;
case FilterInfoRole: case FilterInfoRole:
return QString(item->name + QStringLiteral(" ") return QString(appItem.name + QStringLiteral(" ")
+ item->genericName + appItem.genericName
+ QStringLiteral(" ") + QStringLiteral(" ")
+ item->comment); + appItem.comment);
default: default:
return QVariant(); return QVariant();
} }
@ -137,12 +140,12 @@ void LauncherModel::search(const QString &key)
m_mode = key.isEmpty() ? NormalMode : SearchMode; m_mode = key.isEmpty() ? NormalMode : SearchMode;
m_searchItems.clear(); m_searchItems.clear();
for (LauncherItem *item : qAsConst(m_items)) { for (const AppItem &item : qAsConst(m_appItems)) {
const QString &name = item->name; const QString &name = item.name;
const QString &fileName = item->id; const QString &fileName = item.id;
if (name.contains(key, Qt::CaseInsensitive) || if (name.contains(key, Qt::CaseInsensitive) ||
fileName.contains(key, Qt::CaseInsensitive)) { fileName.contains(key, Qt::CaseInsensitive)) {
m_searchItems.append(item); m_searchItems.append(item);
continue; continue;
} }
@ -181,8 +184,8 @@ void LauncherModel::removeFromDock(const QString &desktop)
int LauncherModel::findById(const QString &id) int LauncherModel::findById(const QString &id)
{ {
for (int i = 0; i < m_items.size(); ++i) { for (int i = 0; i < m_appItems.size(); ++i) {
if (m_items.at(i)->id == id) if (m_appItems.at(i).id == id)
return i; return i;
} }
@ -192,8 +195,8 @@ int LauncherModel::findById(const QString &id)
void LauncherModel::refresh(LauncherModel *manager) void LauncherModel::refresh(LauncherModel *manager)
{ {
QStringList addedEntries; QStringList addedEntries;
for (LauncherItem *item : qAsConst(manager->m_items)) for (const AppItem &item : qAsConst(manager->m_appItems))
addedEntries.append(item->id); addedEntries.append(item.id);
QStringList allEntries; QStringList allEntries;
QDirIterator it("/usr/share/applications", { "*.desktop" }, QDir::NoFilter, QDirIterator::Subdirectories); QDirIterator it("/usr/share/applications", { "*.desktop" }, QDir::NoFilter, QDirIterator::Subdirectories);
@ -207,14 +210,13 @@ void LauncherModel::refresh(LauncherModel *manager)
} }
for (const QString &fileName : allEntries) { for (const QString &fileName : allEntries) {
if (!addedEntries.contains(fileName)) //if (!addedEntries.contains(fileName))
QMetaObject::invokeMethod(manager, "addApp", Q_ARG(QString, fileName)); QMetaObject::invokeMethod(manager, "addApp", Q_ARG(QString, fileName));
} }
for (LauncherItem *item : qAsConst(manager->m_items)) { for (const AppItem &item : qAsConst(manager->m_appItems))
if (!allEntries.contains(item->id)) if (!allEntries.contains(item.id))
QMetaObject::invokeMethod(manager, "removeApp", Q_ARG(LauncherItem *, item)); QMetaObject::invokeMethod(manager, "removeApp", Q_ARG(QString, item.id));
}
// Signal the model was refreshed // Signal the model was refreshed
QMetaObject::invokeMethod(manager, "refreshed"); QMetaObject::invokeMethod(manager, "refreshed");
@ -228,7 +230,7 @@ void LauncherModel::move(int from, int to, int page, int pageCount)
int newFrom = from + (page * pageCount); int newFrom = from + (page * pageCount);
int newTo = to + (page * pageCount); int newTo = to + (page * pageCount);
m_items.move(newFrom, newTo); m_appItems.move(newFrom, newTo);
// if (from < to) // if (from < to)
// beginMoveRows(QModelIndex(), from, from, QModelIndex(), to + 1); // beginMoveRows(QModelIndex(), from, from, QModelIndex(), to + 1);
@ -236,23 +238,34 @@ void LauncherModel::move(int from, int to, int page, int pageCount)
// beginMoveRows(QModelIndex(), from, from, QModelIndex(), to); // beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
// endMoveRows(); // endMoveRows();
delaySave();
} }
void LauncherModel::save() void LauncherModel::save()
{ {
m_settings.clear();
QByteArray datas; QByteArray datas;
QDataStream out(&datas, QIODevice::WriteOnly); QDataStream out(&datas, QIODevice::WriteOnly);
out << m_items; out << m_appItems;
m_settings.setValue("list", datas); m_settings.setValue("list", datas);
} }
void LauncherModel::delaySave()
{
if (m_saveTimer.isActive())
m_saveTimer.stop();
m_saveTimer.start();
}
bool LauncherModel::launch(const QString &path) bool LauncherModel::launch(const QString &path)
{ {
int index = findById(path); int index = findById(path);
if (index != -1) { if (index != -1) {
LauncherItem *item = m_items.at(index); const AppItem &item = m_appItems.at(index);
QStringList args = item->args; QStringList args = item.args;
QScopedPointer<QProcess> p(new QProcess); QScopedPointer<QProcess> p(new QProcess);
p->setStandardInputFile(QProcess::nullDevice()); p->setStandardInputFile(QProcess::nullDevice());
p->setProcessChannelMode(QProcess::ForwardedChannels); p->setProcessChannelMode(QProcess::ForwardedChannels);
@ -275,11 +288,26 @@ bool LauncherModel::launch(const QString &path)
return false; return false;
} }
void LauncherModel::addApp(const QString &fileName) void LauncherModel::onRefreshed()
{ {
if (findById(fileName) != -1) if (!m_needSort)
return; return;
m_needSort = false;
beginResetModel();
std::sort(m_appItems.begin(), m_appItems.end(), [=] (AppItem &a, AppItem &b) {
return a.name < b.name;
});
endResetModel();
delaySave();
}
void LauncherModel::addApp(const QString &fileName)
{
int index = findById(fileName) ;
DesktopProperties desktop(fileName, "Desktop Entry"); DesktopProperties desktop(fileName, "Desktop Entry");
if (desktop.contains("Terminal") && desktop.value("Terminal").toBool()) if (desktop.contains("Terminal") && desktop.value("Terminal").toBool())
@ -308,32 +336,43 @@ void LauncherModel::addApp(const QString &fileName)
appExec = appExec.replace("\"", ""); appExec = appExec.replace("\"", "");
appExec = appExec.simplified(); appExec = appExec.simplified();
LauncherItem *item = new LauncherItem; // 存在需要更新信息
item->id = fileName; if (index >= 0 && index <= m_appItems.size()) {
item->name = appName; AppItem &item = m_appItems[index];
item->genericName = desktop.value("Comment").toString(); item.name = appName;
item->comment = desktop.value("Comment").toString(); item.genericName = desktop.value("Comment").toString();
item->iconName = desktop.value("Icon").toString(); item.comment = desktop.value("Comment").toString();
item->args = appExec.split(" "); item.iconName = desktop.value("Icon").toString();
item.args = appExec.split(" ");
beginInsertRows(QModelIndex(), m_items.count(), m_items.count()); emit dataChanged(LauncherModel::index(index), LauncherModel::index(index));
m_items.append(item); } else {
qDebug() << "added: " << item->name; AppItem appItem;
// Q_EMIT applicationAdded(item); appItem.id = fileName;
endInsertRows(); appItem.name = appName;
appItem.genericName = desktop.value("Comment").toString();
appItem.comment = desktop.value("Comment").toString();
appItem.iconName = desktop.value("Icon").toString();
appItem.args = appExec.split(" ");
beginInsertRows(QModelIndex(), m_appItems.count(), m_appItems.count());
m_appItems.append(appItem);
qDebug() << "added: " << appItem.name;
endInsertRows();
if (!m_needSort)
delaySave();
}
} }
void LauncherModel::removeApp(LauncherItem *item) void LauncherModel::removeApp(const QString &fileName)
{ {
int index = m_items.indexOf(item); int index = findById(fileName);
if (index < 0) if (index < 0)
return; return;
beginRemoveRows(QModelIndex(), index, index); beginRemoveRows(QModelIndex(), index, index);
qDebug() << "removed: " << item->name; m_appItems.removeAt(index);
m_items.removeAt(index);
endRemoveRows(); endRemoveRows();
Q_EMIT applicationRemoved(item); delaySave();
item->deleteLater();
} }

@ -1,7 +1,7 @@
/* /*
* Copyright (C) 2021 CutefishOS. * Copyright (C) 2021 CutefishOS.
* *
* Author: revenmartin <revenmartin@gmail.com> * Author: Reion Wong <reion@cutefishos.com>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -24,8 +24,9 @@
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QSettings> #include <QSettings>
#include <QTimer>
#include "launcheritem.h" #include "appitem.h"
class LauncherModel : public QAbstractListModel class LauncherModel : public QAbstractListModel
{ {
@ -33,8 +34,7 @@ class LauncherModel : public QAbstractListModel
Q_PROPERTY(int count READ count NOTIFY countChanged) Q_PROPERTY(int count READ count NOTIFY countChanged)
public: public:
enum Roles enum Roles {
{
AppIdRole = Qt::UserRole + 1, AppIdRole = Qt::UserRole + 1,
ApplicationRole, ApplicationRole,
NameRole, NameRole,
@ -73,6 +73,8 @@ public:
Q_INVOKABLE void move(int from, int to, int page, int pageCount); Q_INVOKABLE void move(int from, int to, int page, int pageCount);
Q_INVOKABLE void save(); Q_INVOKABLE void save();
void delaySave();
public Q_SLOTS: public Q_SLOTS:
Q_INVOKABLE bool launch(const QString &path); Q_INVOKABLE bool launch(const QString &path);
Q_INVOKABLE bool launch() { return launch(QString()); } Q_INVOKABLE bool launch() { return launch(QString()); }
@ -80,19 +82,22 @@ public Q_SLOTS:
Q_SIGNALS: Q_SIGNALS:
void countChanged(); void countChanged();
void refreshed(); void refreshed();
void applicationAdded(LauncherItem *app);
void applicationRemoved(LauncherItem *app);
void applicationLaunched(); void applicationLaunched();
private Q_SLOTS: private Q_SLOTS:
void onRefreshed();
void addApp(const QString &fileName); void addApp(const QString &fileName);
void removeApp(LauncherItem *item); void removeApp(const QString &fileName);
private: private:
QList<LauncherItem *> m_items; QList<AppItem> m_appItems;
QList<LauncherItem *> m_searchItems; QList<AppItem> m_searchItems;
QTimer m_saveTimer;
QSettings m_settings; QSettings m_settings;
Mode m_mode; Mode m_mode;
bool m_needSort;
}; };
#endif // LAUNCHERMODEL_H #endif // LAUNCHERMODEL_H

@ -25,7 +25,6 @@
#include <QCommandLineParser> #include <QCommandLineParser>
#include "launcher.h" #include "launcher.h"
#include "launcheritem.h"
#include "launchermodel.h" #include "launchermodel.h"
#include "pagemodel.h" #include "pagemodel.h"
#include "iconitem.h" #include "iconitem.h"
@ -43,8 +42,6 @@ int main(int argc, char *argv[])
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QByteArray uri = "Cutefish.Launcher"; QByteArray uri = "Cutefish.Launcher";
qRegisterMetaType<LauncherItem>("LauncherItem");
qmlRegisterType<LauncherItem>(uri, 1, 0, "LauncherItem");
qmlRegisterType<LauncherModel>(uri, 1, 0, "LauncherModel"); qmlRegisterType<LauncherModel>(uri, 1, 0, "LauncherModel");
qmlRegisterType<PageModel>(uri, 1, 0, "PageModel"); qmlRegisterType<PageModel>(uri, 1, 0, "PageModel");
qmlRegisterType<IconItem>(uri, 1, 0, "IconItem"); qmlRegisterType<IconItem>(uri, 1, 0, "IconItem");

Loading…
Cancel
Save