diff --git a/src/libappdata/app-category-plugin.cpp b/src/libappdata/app-category-plugin.cpp
new file mode 100644
index 0000000..4d8c900
--- /dev/null
+++ b/src/libappdata/app-category-plugin.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024, 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 .
+ *
+ * Authors: hxf
+ *
+ */
+
+#include "app-category-plugin.h"
+#include "combined-list-model.h"
+#include "app-category-model.h"
+#include "recently-installed-model.h"
+
+#include
+#include
+
+namespace UkuiMenu {
+
+AppCategoryPlugin::AppCategoryPlugin(QObject *parent) : AppListPluginInterface(parent)
+ , m_dataModel(new CombinedListModel(this))
+{
+ auto categoryModel = new AppCategoryModel(this);
+ auto recentlyModel = new RecentlyInstalledModel(this);
+
+ m_dataModel->insertSubModel(recentlyModel);
+ m_dataModel->insertSubModel(categoryModel);
+
+ auto categoryAction = new QAction(QIcon::fromTheme("applications-utilities-symbolic"), tr("Category"), this);
+ auto firstLatterAction = new QAction(QIcon::fromTheme("ukui-capslock-symbolic"), tr("Letter Sort"), this);
+
+ categoryAction->setCheckable(true);
+ firstLatterAction->setCheckable(true);
+
+ connect(categoryAction, &QAction::triggered, this, [=] {
+ categoryModel->setMode(AppCategoryModel::Category);
+ categoryAction->setChecked(true);
+ firstLatterAction->setChecked(false);
+ setTitle(categoryAction->text());
+ });
+ connect(firstLatterAction, &QAction::triggered, this, [=] {
+ categoryModel->setMode(AppCategoryModel::FirstLatter);
+ categoryAction->setChecked(false);
+ firstLatterAction->setChecked(true);
+ setTitle(firstLatterAction->text());
+ });
+
+ m_actions.append(categoryAction);
+ m_actions.append(firstLatterAction);
+
+ categoryAction->setChecked(true);
+ setTitle(categoryAction->text());
+}
+
+QString AppCategoryPlugin::name()
+{
+ return QStringLiteral("AppCategoryPlugin");
+}
+
+QList AppCategoryPlugin::actions()
+{
+ return m_actions;
+}
+
+QAbstractItemModel *AppCategoryPlugin::dataModel()
+{
+ return m_dataModel;
+}
+
+AppListPluginGroup::Group AppCategoryPlugin::group()
+{
+ return AppListPluginGroup::Display;
+}
+
+QString AppCategoryPlugin::title()
+{
+ return m_title;
+}
+
+void AppCategoryPlugin::setTitle(const QString &title)
+{
+ if (title == m_title) {
+ return;
+ }
+
+ m_title = title;
+ Q_EMIT titleChanged();
+}
+
+} // UkuiMenu
diff --git a/src/libappdata/app-category-plugin.h b/src/libappdata/app-category-plugin.h
new file mode 100644
index 0000000..74b72ee
--- /dev/null
+++ b/src/libappdata/app-category-plugin.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024, 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 .
+ *
+ * Authors: hxf
+ *
+ */
+
+#ifndef UKUI_MENU_APP_CATEGORY_PLUGIN_H
+#define UKUI_MENU_APP_CATEGORY_PLUGIN_H
+
+#include "app-list-plugin.h"
+
+namespace UkuiMenu {
+
+class CombinedListModel;
+
+class AppCategoryPlugin : public AppListPluginInterface
+{
+ Q_OBJECT
+public:
+ explicit AppCategoryPlugin(QObject *parent = nullptr);
+
+ AppListPluginGroup::Group group() override;
+ QString name() override;
+ QString title() override;
+ QList actions() override;
+ QAbstractItemModel *dataModel() override;
+
+private:
+ void setTitle(const QString &title);
+
+private:
+ QString m_title;
+ QList m_actions;
+ CombinedListModel *m_dataModel {nullptr};
+};
+
+} // UkuiMenu
+
+#endif //UKUI_MENU_APP_CATEGORY_PLUGIN_H
diff --git a/src/libappdata/app-list-model.cpp b/src/libappdata/app-list-model.cpp
new file mode 100644
index 0000000..2b85098
--- /dev/null
+++ b/src/libappdata/app-list-model.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2024, 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 .
+ *
+ * Authors: hxf
+ *
+ */
+
+#include "app-list-model.h"
+#include "data-entity.h"
+#include "context-menu-manager.h"
+
+#include
+
+namespace UkuiMenu {
+
+AppListHeader::AppListHeader(QObject *parent) : QObject(parent)
+{
+
+}
+
+QString AppListHeader::title() const
+{
+ return m_title;
+}
+
+void AppListHeader::setTitle(const QString &title)
+{
+ if (title == m_title) {
+ return;
+ }
+
+ m_title = title;
+ Q_EMIT titleChanged();
+}
+
+QList AppListHeader::actions() const
+{
+ return m_actions;
+}
+
+void AppListHeader::addAction(QAction *action)
+{
+ if (m_actions.indexOf(action) >= 0) {
+ return;
+ }
+
+ m_actions.append(action);
+ Q_EMIT actionsChanged();
+ setVisible(true);
+}
+
+bool AppListHeader::visible() const
+{
+ return m_visible;
+}
+
+void AppListHeader::setVisible(bool visible)
+{
+ if (m_visible == visible) {
+ return;
+ }
+
+ m_visible = visible;
+ Q_EMIT visibleChanged();
+}
+
+void AppListHeader::removeAction(QAction *action)
+{
+ if (m_actions.removeOne(action)) {
+ Q_EMIT actionsChanged();
+ setVisible(!m_actions.isEmpty());
+ }
+}
+
+void AppListHeader::removeAllAction()
+{
+ m_actions.clear();
+ Q_EMIT actionsChanged();
+ setVisible(false);
+}
+
+// ====== //
+AppListModel::AppListModel(QObject *parent) : QSortFilterProxyModel(parent), m_header(new AppListHeader(this))
+{
+ qRegisterMetaType();
+ qRegisterMetaType();
+}
+
+QHash AppListModel::roleNames() const
+{
+ return DataEntity::AppRoleNames();
+}
+
+AppListHeader *AppListModel::getHeader() const
+{
+ return m_header;
+}
+
+void AppListModel::installPlugin(AppListPluginInterface *plugin)
+{
+ if (!plugin || m_plugin == plugin) {
+ return;
+ }
+
+ unInstallPlugin();
+
+ m_plugin = plugin;
+ QSortFilterProxyModel::setSourceModel(plugin->dataModel());
+
+ for (const auto &action : plugin->actions()) {
+ m_header->addAction(action);
+ }
+
+ m_header->setTitle(plugin->title());
+ connect(m_plugin, &AppListPluginInterface::titleChanged, this, [this, plugin] {
+ m_header->setTitle(plugin->title());
+ });
+}
+
+void AppListModel::unInstallPlugin()
+{
+ if (!m_plugin) {
+ return;
+ }
+
+ m_header->setTitle("");
+ m_header->removeAllAction();
+ QSortFilterProxyModel::setSourceModel(nullptr);
+ disconnect(m_plugin, nullptr, this, nullptr);
+ m_plugin = nullptr;
+}
+
+void AppListModel::openMenu(const int &index, MenuInfo::Location location) const
+{
+ QModelIndex idx = AppListModel::index(index, 0);
+ if (checkIndex(idx, CheckIndexOption::IndexIsValid)) {
+ ContextMenuManager::instance()->showMenu(idx.data(DataEntity::Entity).value(), location);
+ }
+}
+
+} // UkuiMenu
diff --git a/src/libappdata/app-list-model.h b/src/libappdata/app-list-model.h
new file mode 100644
index 0000000..a2df63d
--- /dev/null
+++ b/src/libappdata/app-list-model.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024, 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 .
+ *
+ * Authors: hxf
+ *
+ */
+
+#ifndef UKUI_MENU_APP_LIST_MODEL_H
+#define UKUI_MENU_APP_LIST_MODEL_H
+
+#include "app-list-plugin.h"
+#include "context-menu-extension.h"
+
+#include
+#include
+
+namespace UkuiMenu {
+
+/**
+ * @class AppListHeader
+ *
+ * 应用列表顶部功能区域
+ * 显示当前插件的操作选项,如果插件的action为空,那么不显示header.
+ */
+class AppListHeader : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool visible READ visible NOTIFY visibleChanged)
+ Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
+ Q_PROPERTY(QList actions READ actions NOTIFY actionsChanged)
+ friend class AppListModel;
+public:
+ explicit AppListHeader(QObject *parent = nullptr);
+
+ bool visible() const;
+ void setVisible(bool visible);
+
+ QString title() const;
+ void setTitle(const QString &title);
+
+ QList actions() const;
+ void addAction(QAction *actions);
+ void removeAction(QAction *actions);
+ void removeAllAction();
+
+Q_SIGNALS:
+ void titleChanged();
+ void actionsChanged();
+ void visibleChanged();
+
+private:
+ bool m_visible {false};
+ QString m_title;
+ QList m_actions;
+};
+
+class AppListModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+ Q_PROPERTY(UkuiMenu::AppListHeader *header READ getHeader NOTIFY headerChanged)
+public:
+ explicit AppListModel(QObject *parent = nullptr);
+
+ /**
+ * 覆盖全部roles,子model必须返回相同的roles
+ * @return
+ */
+ QHash roleNames() const override;
+
+ AppListHeader *getHeader() const;
+
+ void installPlugin(AppListPluginInterface *plugin);
+ // reset
+ void unInstallPlugin();
+
+ Q_INVOKABLE void openMenu(const int &index, MenuInfo::Location location) const;
+
+Q_SIGNALS:
+ void headerChanged();
+
+private:
+ AppListHeader *m_header {nullptr};
+ AppListPluginInterface *m_plugin {nullptr};
+};
+
+} // UkuiMenu
+
+Q_DECLARE_METATYPE(UkuiMenu::AppListModel*)
+Q_DECLARE_METATYPE(UkuiMenu::AppListHeader*)
+
+#endif //UKUI_MENU_APP_LIST_MODEL_H
diff --git a/src/libappdata/app-list-plugin.cpp b/src/libappdata/app-list-plugin.cpp
new file mode 100644
index 0000000..e321fa3
--- /dev/null
+++ b/src/libappdata/app-list-plugin.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024, 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 .
+ *
+ * Authors: hxf
+ *
+ */
+
+#include "app-list-plugin.h"
+#include
+
+namespace UkuiMenu {
+
+// ====== AppListPluginInterface ====== //
+AppListPluginInterface::AppListPluginInterface(QObject *parent) : QObject(parent)
+{
+
+}
+
+void AppListPluginInterface::search(const QString &keyword)
+{
+ Q_UNUSED(keyword)
+}
+
+} // UkuiMenu
diff --git a/src/libappdata/app-list-plugin.h b/src/libappdata/app-list-plugin.h
new file mode 100644
index 0000000..53f56ca
--- /dev/null
+++ b/src/libappdata/app-list-plugin.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024, 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 .
+ *
+ * Authors: hxf
+ *
+ */
+
+#ifndef UKUI_MENU_APP_LIST_PLUGIN_H
+#define UKUI_MENU_APP_LIST_PLUGIN_H
+
+#include
+#include
+
+class QAction;
+class QAbstractItemModel;
+
+namespace UkuiMenu {
+
+class AppListPluginGroup
+{
+ Q_GADGET
+public:
+ enum Group {
+ Display, /**> 应用展示模式 */
+ Search /**> 应用搜索模式 */
+ };
+ Q_ENUM(Group)
+};
+
+class AppListPluginInterface : public QObject
+{
+ Q_OBJECT
+public:
+ explicit AppListPluginInterface(QObject *parent = nullptr);
+
+ virtual AppListPluginGroup::Group group() = 0;
+ virtual QString name() = 0;
+ virtual QString title() = 0;
+ virtual QList actions() = 0;
+ virtual QAbstractItemModel *dataModel() = 0;
+ virtual void search(const QString &keyword);
+
+Q_SIGNALS:
+ void titleChanged();
+};
+
+} // UkuiMenu
+
+Q_DECLARE_METATYPE(UkuiMenu::AppListPluginGroup::Group)
+
+#endif //UKUI_MENU_APP_LIST_PLUGIN_H
diff --git a/src/libappdata/app-page-backend.cpp b/src/libappdata/app-page-backend.cpp
new file mode 100644
index 0000000..3c615c8
--- /dev/null
+++ b/src/libappdata/app-page-backend.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024, 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 .
+ *
+ * Authors: hxf
+ *
+ */
+
+#include "app-page-backend.h"
+
+#include "app-list-model.h"
+#include "app-category-plugin.h"
+#include "app-search-plugin.h"
+
+#include
+
+namespace UkuiMenu {
+
+AppPageBackend::AppPageBackend(QObject *parent) : QObject(parent), m_appModel(new AppListModel(this))
+{
+ auto searchPlugin = new AppSearchPlugin(this);
+ auto categoryPlugin = new AppCategoryPlugin(this);
+
+ m_plugins.insert(searchPlugin->group(), searchPlugin);
+ m_plugins.insert(categoryPlugin->group(), categoryPlugin);
+
+ switchGroup();
+}
+
+AppListModel *AppPageBackend::appModel() const
+{
+ return m_appModel;
+}
+
+void AppPageBackend::startSearch(const QString &keyword)
+{
+ if (m_group != AppListPluginGroup::Search) {
+ return;
+ }
+
+ auto plugin = m_plugins.value(m_group, nullptr);
+ if (plugin) {
+ plugin->search(keyword);
+ }
+}
+
+AppListPluginGroup::Group AppPageBackend::group() const
+{
+ return AppListPluginGroup::Display;
+}
+
+void AppPageBackend::setGroup(AppListPluginGroup::Group group)
+{
+ if (m_group == group) {
+ return;
+ }
+
+ m_group = group;
+ switchGroup();
+ Q_EMIT groupChanged();
+}
+
+void AppPageBackend::switchGroup()
+{
+ const auto plugin = m_plugins.value(m_group, nullptr);
+ if (plugin) {
+ m_appModel->installPlugin(plugin);
+ } else {
+ m_appModel->unInstallPlugin();
+ }
+}
+
+} // UkuiMenu
diff --git a/src/libappdata/app-page-backend.h b/src/libappdata/app-page-backend.h
new file mode 100644
index 0000000..613114c
--- /dev/null
+++ b/src/libappdata/app-page-backend.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024, 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 .
+ *
+ * Authors: hxf
+ *
+ */
+
+#ifndef UKUI_MENU_APP_PAGE_BACKEND_H
+#define UKUI_MENU_APP_PAGE_BACKEND_H
+
+#include
+#include
+class QAbstractItemModel;
+class QSortFilterProxyModel;
+
+#include "app-list-plugin.h"
+
+namespace UkuiMenu {
+
+class AppListModel;
+
+/**
+ * 此类管理开始菜单的主体功能和接口
+ *
+ * 提供:应用搜索,应用分类模式的切换功能
+ *
+ *
+ * AppPage
+ * |
+ * AppListHeader
+ * AppList
+ * / \
+ * Search Display
+ * | \
+ * (Model Title Actions) (Model Title Actions)
+ *
+ */
+class AppPageBackend : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QAbstractItemModel *appModel READ appModel NOTIFY appModelChanged)
+ Q_PROPERTY(UkuiMenu::AppListPluginGroup::Group group READ group WRITE setGroup NOTIFY groupChanged)
+public:
+ explicit AppPageBackend(QObject *parent = nullptr);
+
+ // 开始菜单主要功能,显示应用列表
+ AppListModel *appModel() const;
+ Q_INVOKABLE void startSearch(const QString &keyword);
+
+ AppListPluginGroup::Group group() const;
+ void setGroup(AppListPluginGroup::Group group);
+
+Q_SIGNALS:
+ void appModelChanged();
+ void groupChanged();
+
+private:
+ void switchGroup();
+
+private:
+ AppListPluginGroup::Group m_group {AppListPluginGroup::Display};
+ AppListModel *m_appModel {nullptr};
+ QMap m_plugins;
+
+};
+
+} // UkuiMenu
+
+#endif //UKUI_MENU_APP_PAGE_BACKEND_H
diff --git a/src/libappdata/app-search-plugin.cpp b/src/libappdata/app-search-plugin.cpp
new file mode 100644
index 0000000..ee59541
--- /dev/null
+++ b/src/libappdata/app-search-plugin.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2024, 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 .
+ *
+ * Authors: hxf
+ *
+ */
+#include "app-search-plugin.h"
+#include "data-entity.h"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace UkuiMenu {
+
+// ====== AppSearchPluginPrivate ======
+class AppSearchPluginPrivate : public QThread
+{
+ Q_OBJECT
+public:
+ explicit AppSearchPluginPrivate(QObject *parent = nullptr);
+
+Q_SIGNALS:
+ void searchedOne(DataEntity app);
+
+public Q_SLOTS:
+ void startSearch(const QString &keyword);
+ void stopSearch();
+
+protected:
+ void run() override;
+
+private:
+ size_t m_searchId{0};
+ QTimer *m_timer{nullptr};
+ UkuiSearch::UkuiSearchTask *m_appSearchTask {nullptr};
+ UkuiSearch::DataQueue *m_dataQueue{nullptr};
+};
+
+AppSearchPluginPrivate::AppSearchPluginPrivate(QObject *parent) : QThread(parent), m_appSearchTask(new UkuiSearch::UkuiSearchTask(this))
+{
+ m_dataQueue = m_appSearchTask->init();
+
+ m_appSearchTask->initSearchPlugin(UkuiSearch::SearchProperty::SearchType::Application);
+ m_appSearchTask->setSearchOnlineApps(false);
+
+ UkuiSearch::SearchResultProperties searchResultProperties;
+ searchResultProperties << UkuiSearch::SearchProperty::SearchResultProperty::ApplicationDesktopPath
+ << UkuiSearch::SearchProperty::SearchResultProperty::ApplicationLocalName
+ << UkuiSearch::SearchProperty::SearchResultProperty::ApplicationIconName;
+ m_appSearchTask->setResultProperties(UkuiSearch::SearchProperty::SearchType::Application, searchResultProperties);
+
+ m_timer = new QTimer;
+ m_timer->setInterval(3000);
+ m_timer->moveToThread(this);
+}
+
+void AppSearchPluginPrivate::startSearch(const QString &keyword)
+{
+ if (!this->isRunning()) {
+ this->start();
+ }
+
+ m_appSearchTask->clearKeyWords();
+ m_appSearchTask->addKeyword(keyword);
+ m_searchId = m_appSearchTask->startSearch(UkuiSearch::SearchProperty::SearchType::Application);
+}
+
+void AppSearchPluginPrivate::stopSearch()
+{
+ m_appSearchTask->stop();
+ this->requestInterruption();
+}
+
+void AppSearchPluginPrivate::run()
+{
+ while (!isInterruptionRequested()) {
+ UkuiSearch::ResultItem result = m_dataQueue->tryDequeue();
+ if(result.getSearchId() == 0 && result.getItemKey().isEmpty() && result.getAllValue().isEmpty()) {
+ if(!m_timer->isActive()) {
+ // 超时退出
+ m_timer->start();
+ }
+ msleep(100);
+ } else {
+ m_timer->stop();
+ if (result.getSearchId() == m_searchId) {
+ DataEntity app;
+ app.setType(DataType::Normal);
+ app.setId(result.getValue(UkuiSearch::SearchProperty::ApplicationDesktopPath).toString());
+ app.setName(result.getValue(UkuiSearch::SearchProperty::ApplicationLocalName).toString());
+ app.setIcon("image://appicon/" + result.getValue(UkuiSearch::SearchProperty::ApplicationIconName).toString());
+
+ Q_EMIT this->searchedOne(app);
+ }
+ }
+
+ if(m_timer->isActive() && m_timer->remainingTime() < 0.01 && m_dataQueue->isEmpty()) {
+ this->requestInterruption();
+ }
+ }
+}
+
+// ====== AppSearchModel ====== //
+class AppSearchModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ explicit AppSearchModel(QObject *parent = nullptr);
+ int rowCount(const QModelIndex &parent) const override;
+ int columnCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+ void appendApp(const DataEntity &app);
+ void clear();
+
+private:
+ QVector m_apps;
+};
+
+AppSearchModel::AppSearchModel(QObject *parent) : QAbstractListModel(parent)
+{
+
+}
+
+int AppSearchModel::rowCount(const QModelIndex &parent) const
+{
+ return m_apps.size();
+}
+
+int AppSearchModel::columnCount(const QModelIndex &parent) const
+{
+ return 1;
+}
+
+QVariant AppSearchModel::data(const QModelIndex &index, int role) const
+{
+ if (!checkIndex(index, CheckIndexOption::IndexIsValid)) {
+ return {};
+ }
+
+ const DataEntity &app = m_apps[index.row()];
+ return app.getValue(static_cast(role));
+}
+
+void AppSearchModel::appendApp(const DataEntity &app)
+{
+ beginInsertRows(QModelIndex(), m_apps.size(), m_apps.size());
+ m_apps.append(app);
+ endInsertRows();
+}
+
+void AppSearchModel::clear()
+{
+ beginResetModel();
+ m_apps.clear();
+ endResetModel();
+}
+
+// ====== AppSearchPlugin ====== //
+AppSearchPlugin::AppSearchPlugin(QObject *parent) : AppListPluginInterface(parent)
+ , m_searchPluginPrivate(new AppSearchPluginPrivate(this)), m_model(new AppSearchModel(this))
+{
+ connect(m_searchPluginPrivate, &AppSearchPluginPrivate::searchedOne, m_model, &AppSearchModel::appendApp);
+}
+
+AppListPluginGroup::Group AppSearchPlugin::group()
+{
+ return AppListPluginGroup::Search;
+}
+
+QString AppSearchPlugin::name()
+{
+ return "Search";
+}
+
+QString AppSearchPlugin::title()
+{
+ return "Search";
+}
+
+QList AppSearchPlugin::actions()
+{
+ // TODO: 搜索结果排序选项
+ return {};
+}
+
+QAbstractItemModel *AppSearchPlugin::dataModel()
+{
+ return m_model;
+}
+
+void AppSearchPlugin::search(const QString &keyword)
+{
+ m_model->clear();
+ if (keyword.isEmpty()) {
+ return;
+ }
+
+ m_searchPluginPrivate->startSearch(keyword);
+}
+
+AppSearchPlugin::~AppSearchPlugin()
+{
+ m_searchPluginPrivate->stopSearch();
+ m_searchPluginPrivate->quit();
+ m_searchPluginPrivate->wait();
+ m_searchPluginPrivate->deleteLater();
+}
+
+} // UkuiMenu
+
+#include "app-search-plugin.moc"
diff --git a/src/libappdata/app-search-plugin.h b/src/libappdata/app-search-plugin.h
new file mode 100644
index 0000000..d870369
--- /dev/null
+++ b/src/libappdata/app-search-plugin.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024, 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 .
+ *
+ * Authors: hxf
+ *
+ */
+
+#ifndef UKUI_MENU_APP_SEARCH_PLUGIN_H
+#define UKUI_MENU_APP_SEARCH_PLUGIN_H
+
+#include "app-list-plugin.h"
+
+namespace UkuiMenu {
+
+class AppSearchPluginPrivate;
+class AppSearchModel;
+
+/**
+ * @class AppSearchPlugin
+ * 应用搜索插件,调用搜索接口进行应用搜索
+ */
+class AppSearchPlugin : public AppListPluginInterface
+{
+ Q_OBJECT
+public:
+ explicit AppSearchPlugin(QObject *parent = nullptr);
+ ~AppSearchPlugin() override;
+
+ AppListPluginGroup::Group group() override;
+ QString name() override;
+ QString title() override;
+ QList actions() override;
+ QAbstractItemModel *dataModel() override;
+ void search(const QString &keyword) override;
+
+private:
+ AppSearchModel *m_model {nullptr};
+ AppSearchPluginPrivate * m_searchPluginPrivate {nullptr};
+};
+
+} // UkuiMenu
+
+#endif //UKUI_MENU_APP_SEARCH_PLUGIN_H