forked from openkylin/ukui-menu
增加应用分类功能插件
This commit is contained in:
parent
8766051c28
commit
98050e0e2f
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
Loading…
Reference in New Issue