增加应用分类功能插件

This commit is contained in:
hewenfei 2023-03-01 18:03:50 +08:00
parent 8766051c28
commit 98050e0e2f
8 changed files with 391 additions and 1 deletions

View File

@ -91,6 +91,7 @@ set(SOURCE_FILES
src/appdata/data-provider-manager.cpp src/appdata/data-provider-manager.h
src/appdata/plugin/all-app-data-provider.cpp src/appdata/plugin/all-app-data-provider.h
src/appdata/plugin/app-search-plugin.cpp src/appdata/plugin/app-search-plugin.h
src/appdata/plugin/app-category-plugin.cpp src/appdata/plugin/app-category-plugin.h
src/extension/menu-extension-iface.h
src/extension/menu-extension.cpp src/extension/menu-extension.h
src/extension/extensions/folder-extension.cpp src/extension/extensions/folder-extension.h

View File

@ -19,6 +19,7 @@
#include "data-provider-manager.h"
#include "plugin/all-app-data-provider.h"
#include "plugin/app-search-plugin.h"
#include "plugin/app-category-plugin.h"
namespace UkuiMenu {
@ -47,9 +48,13 @@ void DataProviderManager::initProviders()
auto *search = new AppSearchPlugin;
registerProvider(search);
auto *category = new AppCategoryPlugin;
registerProvider(category);
activateProvider(allProvider->id());
allProvider->moveToThread(&m_worker);
search->moveToThread(&m_worker);
category->moveToThread(&m_worker);
}
void DataProviderManager::registerProvider(DataProviderPluginIFace *provider)

View File

@ -71,6 +71,7 @@ public:
* @return
*/
virtual QVector<DataEntity> data() = 0;
virtual QVector<LabelItem> labels() { return {}; };
/**
* dataChange信号
@ -83,6 +84,7 @@ Q_SIGNALS:
*
*/
void dataChanged(QVector<DataEntity> data, DataUpdateMode::Mode mode = DataUpdateMode::Reset, quint32 index = 0);
void labelChanged();
};
} // UkuiMenu

View File

@ -0,0 +1,239 @@
/*
* Copyright (C) 2023, KylinSoft Co., Ltd.
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "app-category-plugin.h"
#include "app-data-manager.h"
namespace UkuiMenu {
// 临时使用,可能会删除
class CategoryPair
{
public:
CategoryPair() = default;
CategoryPair(QString id_t, QString localName_t) : id(std::move(id_t)), localName(std::move(localName_t)) {}
QString id;
QString localName;
};
class CategoryItem
{
public:
LabelItem label;
QList<DataEntity> apps;
};
class AppCategoryPluginPrivate
{
public:
QHash<QString, int> categoryIndex;
QList<CategoryItem> categoryData;
};
AppCategoryPlugin::AppCategoryPlugin() : d(new AppCategoryPluginPrivate)
{
initCategories();
reLoadApps();
connect(AppDataManager::instance(), &AppDataManager::appAdded, this, &AppCategoryPlugin::onAppAdded);
connect(AppDataManager::instance(), &AppDataManager::appDeleted, this, &AppCategoryPlugin::onAppDeleted);
}
AppCategoryPlugin::~AppCategoryPlugin()
{
if (d) {
delete d;
d = nullptr;
}
}
int AppCategoryPlugin::index()
{
return 1;
}
QString AppCategoryPlugin::id()
{
return "category";
}
QString AppCategoryPlugin::name()
{
return tr("Category");
}
QString AppCategoryPlugin::icon()
{
return "image://appicon/text-plain";
}
QString AppCategoryPlugin::title()
{
return "Category";
}
PluginGroup::Group AppCategoryPlugin::group()
{
return PluginGroup::SortMenuItem;
}
QVector<DataEntity> AppCategoryPlugin::data()
{
QVector<DataEntity> data;
generateData(data);
return data;
}
void AppCategoryPlugin::forceUpdate()
{
}
QVector<LabelItem> AppCategoryPlugin::labels()
{
QMutexLocker locker(&m_mutex);
QVector<LabelItem> labels;
for (const auto &item : d->categoryData) {
// qDebug() << "==label==" << item.label.id() << item.label.displayName();
labels.append(item.label);
}
return labels;
}
// see: https://specifications.freedesktop.org/menu-spec/latest/apa.html
// see: https://specifications.freedesktop.org/menu-spec/latest/apas02.html
// TODO: 暂时只列出freedesktop规范中的常用分类需要继续支持显示应用自定义的分类进行显示
void AppCategoryPlugin::initCategories()
{
QList<CategoryPair> primaryCategories;
primaryCategories.append({"Audio", tr("Audio")});
primaryCategories.append({"AudioVideo", tr("AudioVideo")});
primaryCategories.append({"Development", tr("Development")});
primaryCategories.append({"Education", tr("Education")});
primaryCategories.append({"Game", tr("Game")});
primaryCategories.append({"Graphics", tr("Graphics")});
primaryCategories.append({"Network", tr("Network")});
primaryCategories.append({"Office", tr("Office")});
primaryCategories.append({"Science", tr("Science")});
primaryCategories.append({"Settings", tr("Settings")});
primaryCategories.append({"System", tr("System")});
primaryCategories.append({"Utility", tr("Utility")});
primaryCategories.append({"Video", tr("Video")});
// 未定义Category的应用类别
m_otherLabelId = "Other";
primaryCategories.append({m_otherLabelId, tr("Other")});
for (const auto &category : primaryCategories) {
// 默认拷贝
CategoryItem categoryItem;
categoryItem.label.setId(category.id);
categoryItem.label.setDisplayName(category.localName);
// categoryItem.label.setDisable(true);
categoryItem.label.setIndex(d->categoryData.size());
d->categoryData.append(categoryItem);
d->categoryIndex.insert(category.id, categoryItem.label.index());
}
}
int AppCategoryPlugin::parseAppCategory(const DataEntity &app)
{
// qDebug() << "=parseAppCategory=" << app.name();
// TODO: 某些应用的Category字段不是使用;进行分割的,是否需要支持?
QStringList categories = app.category().split(";");
for (const auto &category : categories) {
int index = d->categoryIndex.value(category, -1);
if (index != -1) {
return index;
}
}
// qDebug() << "Other index:" << d->categoryIndex.value(m_otherLabelId) << "==" << m_otherLabelId;
return d->categoryIndex.value(m_otherLabelId);
}
void AppCategoryPlugin::addAppToCategoryList(const DataEntity &app)
{
CategoryItem &categoryItem = d->categoryData[parseAppCategory(app)];
categoryItem.label.setDisable(false);
categoryItem.apps.append(app);
}
void AppCategoryPlugin::reLoadApps()
{
QMutexLocker locker(&m_mutex);
for (auto &item : d->categoryData) {
item.label.setDisable(true);
}
QList<DataEntity> apps = AppDataManager::instance()->normalApps();
for (const auto &app : apps) {
addAppToCategoryList(app);
}
}
void AppCategoryPlugin::generateData(QVector<DataEntity> &data)
{
QMutexLocker locker(&m_mutex);
for (const auto &item : d->categoryData) {
// TODO: 显示不包含应用的分类?
if (item.apps.isEmpty()) {
continue;
}
DataEntity label;
label.setType(DataType::Label);
label.setId(item.label.id());
label.setName(item.label.displayName());
data.append(label);
// TODO: 性能优化
// data.append(item.apps.toVector());
for (const auto &app : item.apps) {
data.append(app);
}
}
}
void AppCategoryPlugin::updateData()
{
Q_EMIT dataChanged(data());
}
void AppCategoryPlugin::onAppAdded(const QList<DataEntity>& apps)
{
{
QMutexLocker locker(&m_mutex);
for (const auto &app : apps) {
addAppToCategoryList(app);
}
}
updateData();
}
void AppCategoryPlugin::onAppDeleted(const QStringList& idList)
{
reLoadApps();
updateData();
Q_EMIT labelChanged();
}
} // UkuiMenu

View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2023, KylinSoft Co., Ltd.
*
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#ifndef UKUI_MENU_APP_CATEGORY_PLUGIN_H
#define UKUI_MENU_APP_CATEGORY_PLUGIN_H
#include "data-provider-plugin-iface.h"
namespace UkuiMenu {
class AppCategoryPluginPrivate;
class AppCategoryPlugin : public DataProviderPluginIFace
{
Q_OBJECT
public:
AppCategoryPlugin();
~AppCategoryPlugin() override;
int index() override;
QString id() override;
QString name() override;
QString icon() override;
QString title() override;
PluginGroup::Group group() override;
QVector<DataEntity> data() override;
void forceUpdate() override;
QVector<LabelItem> labels() override;
private Q_SLOTS:
void onAppAdded(const QList<DataEntity>& apps);
void onAppDeleted(const QStringList& idList);
private:
void reLoadApps();
void initCategories();
// 解析Category获取正确的index
int parseAppCategory(const DataEntity &app);
void addAppToCategoryList(const DataEntity &app);
// 从私有数据类型生成数据
void generateData(QVector<DataEntity> &data);
inline void updateData();
private:
QMutex m_mutex;
QString m_otherLabelId;
AppCategoryPluginPrivate *d{nullptr};
};
} // UkuiMenu
#endif //UKUI_MENU_APP_CATEGORY_PLUGIN_H

View File

@ -145,3 +145,47 @@ QString UkuiMenu::DataEntity::extraData() const
{
return m_extraData;
}
// ====== LabelItem ====== //
UkuiMenu::LabelItem::LabelItem(bool disable, int index, QString id, QString displayName)
: m_disable(disable), m_index(index), m_id(std::move(id)), m_displayName(std::move(displayName)) {}
bool UkuiMenu::LabelItem::isDisable() const
{
return m_disable;
}
void UkuiMenu::LabelItem::setDisable(bool disable)
{
LabelItem::m_disable = disable;
}
int UkuiMenu::LabelItem::index() const
{
return m_index;
}
void UkuiMenu::LabelItem::setIndex(int index)
{
LabelItem::m_index = index;
}
const QString &UkuiMenu::LabelItem::displayName() const
{
return m_id;
}
void UkuiMenu::LabelItem::setDisplayName(const QString &name)
{
LabelItem::m_id = name;
}
const QString &UkuiMenu::LabelItem::id() const
{
return m_id;
}
void UkuiMenu::LabelItem::setId(const QString &id)
{
m_id = id;
}

View File

@ -24,6 +24,37 @@
namespace UkuiMenu {
// 标签项
class LabelItem
{
Q_GADGET
public:
LabelItem() = default;
explicit LabelItem(bool disable, int index, QString id, QString displayName);
friend inline bool operator==(const LabelItem& a, const LabelItem& b) {
return QString::compare(a.id(), b.id(), Qt::CaseInsensitive);
}
bool isDisable() const;
void setDisable(bool disable);
int index() const;
void setIndex(int index);
const QString &id() const;
void setId(const QString &id);
const QString &displayName() const;
void setDisplayName(const QString &name);
private:
bool m_disable{true};
int m_index{0};
QString m_id;
QString m_displayName;
};
class Display {
Q_GADGET
public:

View File

@ -39,7 +39,7 @@ MenuExtension::MenuExtension()
// TODO load extension from filesystem.
// register extension.
registerExtension(new FolderExtension(this));
// registerExtension(new FolderExtension(this));
registerExtension(new RecentFileExtension(this));
registerExtension(new FavoriteExtension(this));