diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4b5d796..b5368c9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -121,7 +121,13 @@ set(SOURCE_FILES
src/menu/menu-manager.cpp src/menu/menu-manager.h
src/items/theme-icon.h src/items/theme-icon.cpp
src/settings/user-config.cpp src/settings/user-config.h
- )
+ src/extension/widget-extension.cpp src/extension/widget-extension.h
+ src/extension/context-menu-extension.cpp src/extension/context-menu-extension.h
+ src/extension/menu-extension-loader.cpp src/extension/menu-extension-loader.h
+ src/extension/menu-extension-plugin.cpp src/extension/menu-extension-plugin.h
+ src/extension/widget-extension-model.cpp src/extension/widget-extension-model.h
+ src/extension/widget-model.cpp src/extension/widget-model.h
+)
if(COMMAND qt_add_dbus_adaptor)
@@ -141,6 +147,9 @@ set(LIBRARY_HEADERS
src/data-entity.h
src/menu/menu-provider.h
src/extension/menu-extension-iface.h
+ src/extension/menu-extension-plugin.h
+ src/extension/widget-extension.h
+ src/extension/context-menu-extension.h
)
# qrc文件
diff --git a/qml/AppUI/WidgetPage.qml b/qml/AppUI/WidgetPage.qml
index c10c019..5584e39 100644
--- a/qml/AppUI/WidgetPage.qml
+++ b/qml/AppUI/WidgetPage.qml
@@ -29,54 +29,103 @@ import org.ukui.menu.extension 1.0
Item {
ColumnLayout {
- id: sidebarLayout
-
anchors.fill: parent
anchors.topMargin: 12
- spacing: 0
+ spacing: 5
Item {
- id: sidebarTopBar
+ // header
Layout.fillWidth: true
Layout.preferredHeight: 40
- Layout.rightMargin: 12
- Layout.leftMargin: 12
+ height: 40
RowLayout {
anchors.fill: parent
- spacing: 4
+ anchors.leftMargin: 12
+ anchors.rightMargin:12
+ spacing: 0
ListView {
- id: extensionInfoList
-
+ id: widgetList
Layout.fillWidth: true
Layout.fillHeight: true
- Layout.leftMargin: 4
- Layout.rightMargin: 4
clip: true
spacing: 24
interactive: false
orientation: ListView.Horizontal
- model: extensionManager.extensionModel()
- delegate: extensionInfoDelegate
-
function send(data) {
if (currentItem !== null) {
model.send(currentIndex, data);
}
}
-
onCurrentIndexChanged: {
if (currentItem !== null) {
currentItem.select();
}
}
+ model: WidgetModel {}
+ delegate: Component {
+ // 插件信息
+ UkuiItems.StyleBackground {
+ useStyleTransparency: false
+ paletteRole: Palette.Highlight
+ alpha: 0
+ radius: UkuiItems.Theme.minRadius
+ borderColor: Palette.Highlight
+ border.width: activeFocus ? 2 : 0
+
+ property var extensionData: model.data
+ property var extensionOptions: model.options
+ width: styleText.width
+ height: ListView.view ? ListView.view.height : 0
+
+ activeFocusOnTab: true
+ KeyNavigation.down: widgetLoader
+ Keys.onPressed: {
+ if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
+ widgetList.currentIndex = model.index;
+ EventTrack.sendClickEvent("switch_plugin", "Sidebar", {"plugin": model.name});
+ }
+ }
+
+ onExtensionDataChanged: {
+ if (widgetLoader.source === model.main) {
+ widgetLoader.item.extensionData = extensionData;
+ }
+ }
+
+ function select() {
+ if (widgetLoader.source !== model.main) {
+ widgetLoader.setSource(model.main, {extensionData: extensionData});
+ }
+ }
+
+ UkuiItems.StyleText {
+ height: parent.height
+ id: styleText
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ font.bold: parent.ListView.isCurrentItem
+
+ paletteRole: parent.ListView.isCurrentItem ? UkuiItems.Theme.Highlight: UkuiItems.Theme.Text
+ text: model.name
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ normalUI.focusToFalse();
+ widgetList.currentIndex = model.index;
+ EventTrack.sendClickEvent("switch_plugin", "Sidebar", {"plugin": model.name});
+ }
+ }
+ }
+ }
}
Loader {
- id: extensionMenuLoader
- visible: sourceComponent !== undefined
+ id: widgetMenuLoader
Layout.preferredWidth: 34
Layout.preferredHeight: 34
Layout.alignment: Qt.AlignVCenter
@@ -84,100 +133,39 @@ Item {
}
}
- Item {
+ Loader {
+ id: widgetLoader
+ // 加载插件区域
Layout.fillWidth: true
Layout.fillHeight: true
+ clip: true
- Loader {
- id: extensionLoader
- anchors.fill: parent
- clip: true
- onLoaded: {
- item.send.connect(extensionInfoList.send);
- // sidebarLayout.updateSidebarLayout(extensionInfoList.currentItem.extensionOptions);
- updateMenu();
- item.extensionMenuChanged.connect(updateMenu);
- }
- Keys.onTabPressed: {
- extensionInfoList.focus = true
- }
-
- function updateMenu() {
- if (item === null) {
- return;
- }
- if (item.extensionMenu !== null) {
- extensionMenuLoader.sourceComponent = item.extensionMenu;
- } else {
- extensionMenuLoader.sourceComponent = undefined;
- }
- }
+ onLoaded: {
+ item.send.connect(widgetList.send);
+ // sidebarLayout.updateSidebarLayout(widgetList.currentItem.extensionOptions);
+ updateMenu();
+ item.extensionMenuChanged.connect(updateMenu);
}
- }
- }
-
- Component {
- id: extensionInfoDelegate
-
- AppControls2.StyleBackground {
- useStyleTransparent: false
- paletteRole: Palette.Highlight
- alpha: 0
- radius: UkuiItems.Theme.minRadius
- borderColor: Palette.Highlight
- border.width: activeFocus ? 2 : 0
-
- property var extensionData: model.data
- property var extensionOptions: model.options
- width: styleText.width
- height: ListView.view ? ListView.view.height : 0
-
- activeFocusOnTab: true
- KeyNavigation.down: extensionLoader
- Keys.onPressed: {
- if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
- ListView.view.currentIndex = model.index;
- EventTrack.sendClickEvent("switch_plugin", "Sidebar", {"plugin": model.name});
- }
+ Keys.onTabPressed: {
+ widgetList.focus = true
}
- onExtensionDataChanged: {
- if (extensionLoader.source === model.url) {
- extensionLoader.item.extensionData = extensionData;
+ function updateMenu() {
+ if (item === null) {
+ return;
}
- }
-
- function select() {
- if (extensionLoader.source !== model.url) {
- extensionLoader.setSource(model.url, {extensionData: extensionData});
- }
- }
-
- UkuiItems.StyleText {
- height: parent.height
- id: styleText
- verticalAlignment: Text.AlignVCenter
- horizontalAlignment: Text.AlignHCenter
- font.bold: parent.ListView.isCurrentItem
-
- paletteRole: parent.ListView.isCurrentItem ? UkuiItems.Theme.Highlight: UkuiItems.Theme.Text
- text: model.name
- }
-
- MouseArea {
- anchors.fill: parent
- onClicked: {
- normalUI.focusToFalse();
- parent.ListView.view.currentIndex = model.index;
- EventTrack.sendClickEvent("switch_plugin", "Sidebar", {"plugin": model.name});
+ if (item.extensionMenu !== null) {
+ widgetMenuLoader.sourceComponent = item.extensionMenu;
+ } else {
+ widgetMenuLoader.sourceComponent = undefined;
}
}
}
}
Component.onCompleted: {
- if (extensionInfoList.count > 0) {
- extensionInfoList.currentIndex = 0;
+ if (widgetList.count > 0) {
+ widgetList.currentIndex = 0;
}
}
}
diff --git a/src/extension/context-menu-extension.cpp b/src/extension/context-menu-extension.cpp
new file mode 100644
index 0000000..024f9f0
--- /dev/null
+++ b/src/extension/context-menu-extension.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 .
+ *
+ */
+
+#include "context-menu-extension.h"
+
+namespace UkuiMenu {
+
+int ContextMenuExtension::index() const
+{
+ return -1;
+}
+
+} // UkuiMenu
diff --git a/src/extension/context-menu-extension.h b/src/extension/context-menu-extension.h
new file mode 100644
index 0000000..f1b0e58
--- /dev/null
+++ b/src/extension/context-menu-extension.h
@@ -0,0 +1,72 @@
+/*
+ * 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 .
+ *
+ */
+
+#ifndef UKUI_MENU_CONTEXT_MENU_EXTENSION_H
+#define UKUI_MENU_CONTEXT_MENU_EXTENSION_H
+
+#include
+#include
+#include "data-entity.h"
+
+namespace UkuiMenu {
+
+class MenuInfo
+{
+ Q_GADGET
+public:
+ enum Location {
+ AppList = 0, /**< 小屏幕的应用列表 */
+ Extension, /**< 扩展页 */
+ FolderPage, /**< 文件夹页面 */
+ FullScreen, /**< 全屏应用列表 */
+ FullScreenFolder, /**< 全屏文件夹页面 */
+ };
+ Q_ENUM(Location)
+};
+
+/**
+ * @class ContextMenuExtension
+ *
+ * 开始菜单应用列表的上下文菜单扩展插件
+ */
+class ContextMenuExtension
+{
+public:
+ virtual ~ContextMenuExtension() = default;
+ /**
+ * 控制菜单项显示在哪个位置
+ * 对于第三方项目们应该选择-1,或者大于1000的值
+ * @return -1:表示随机放在最后
+ */
+ virtual int index() const;
+
+ /**
+ * 根据data生成action,或者子菜单
+ *
+ * @param data app信息
+ * @param parent action最终显示的QMenu
+ * @param location 请求菜单的位置
+ * @param locationId 位置的描述信息,可选的值有:all,category,letterSort和favorite等插件的id
+ * @return
+ */
+ virtual QList actions(const DataEntity &data, QMenu *parent, const MenuInfo::Location &location, const QString &locationId) = 0;
+};
+
+} // UkuiMenu
+
+#endif //UKUI_MENU_CONTEXT_MENU_EXTENSION_H
diff --git a/src/extension/menu-extension-loader.cpp b/src/extension/menu-extension-loader.cpp
new file mode 100644
index 0000000..7b355b4
--- /dev/null
+++ b/src/extension/menu-extension-loader.cpp
@@ -0,0 +1,137 @@
+/*
+ * 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 .
+ *
+ */
+
+#include "menu-extension-loader.h"
+#include "menu-extension-plugin.h"
+
+#include
+#include
+
+namespace UkuiMenu {
+
+QHash MenuExtensionLoader::plugins = QHash();
+
+MenuExtensionLoader *MenuExtensionLoader::instance()
+{
+ static MenuExtensionLoader loader;
+ return &loader;
+}
+
+MenuExtensionLoader::MenuExtensionLoader()
+{
+// setBlackList({});
+ load();
+}
+
+void MenuExtensionLoader::load()
+{
+ loadInternalExtension();
+ loadExtensionFromDisk();
+
+ expand();
+}
+
+void MenuExtensionLoader::loadInternalExtension()
+{
+
+}
+
+void MenuExtensionLoader::loadExtensionFromDisk()
+{
+ QDir pluginsDir(UKUI_MENU_EXTENSION_DIR);
+ for(const QString& fileName : pluginsDir.entryList({"*.so"},QDir::Files)) {
+ QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
+ QJsonObject metaData = pluginLoader.metaData().value("MetaData").toObject();
+ QString type = metaData.value("Type").toString();
+ QString version = metaData.value("Version").toString();
+ if(type != UKUI_MENU_EXTENSION_I_FACE_TYPE) {
+ continue;
+ }
+
+ if(version != UKUI_MENU_EXTENSION_I_FACE_VERSION) {
+ qWarning() << "UKUI_MENU_EXTENSION version check failed:" << fileName << "version:" << version << "iface version : " << UKUI_MENU_EXTENSION_I_FACE_VERSION;
+ continue;
+ }
+
+ QObject *obj = pluginLoader.instance();
+ if (!obj) {
+ continue;
+ }
+
+ MenuExtensionPlugin *plugin = qobject_cast(obj);
+ if (!plugin) {
+ continue;
+ }
+
+ registerExtension(plugin);
+ }
+}
+
+void MenuExtensionLoader::expand()
+{
+ for (const auto &plugin : MenuExtensionLoader::plugins) {
+// qDebug() << "Expand Extension:" << plugin->id();
+ WidgetExtension *widget = plugin->createWidgetExtension();
+ if (widget) {
+ // register widget.
+ m_widgets.append(widget);
+ }
+
+ ContextMenuExtension *contextMenu = plugin->createContextMenuExtension();
+ if (contextMenu) {
+ // 注册菜单
+ m_menus.append(contextMenu);
+ }
+ }
+
+ std::sort(m_widgets.begin(), m_widgets.end(), [] (const WidgetExtension *a, const WidgetExtension *b) {
+ return a->index() <= b->index();
+ });
+
+ std::sort(m_menus.begin(), m_menus.end(), [] (const ContextMenuExtension *a, const ContextMenuExtension *b) {
+ return a->index() <= b->index();
+ });
+}
+
+void MenuExtensionLoader::setBlackList(const QStringList &blackList)
+{
+ m_blackList = blackList;
+}
+
+QList MenuExtensionLoader::widgets() const
+{
+ return m_widgets;
+}
+
+QList MenuExtensionLoader::menus() const
+{
+ return m_menus;
+}
+
+void MenuExtensionLoader::registerExtension(MenuExtensionPlugin *plugin)
+{
+ QString id = plugin->id();
+ if (m_blackList.contains(id) || MenuExtensionLoader::plugins.contains(id)) {
+ delete plugin;
+ return;
+ }
+
+ MenuExtensionLoader::plugins.insert(id, plugin);
+}
+
+} // UkuiMenu
diff --git a/src/extension/menu-extension-loader.h b/src/extension/menu-extension-loader.h
new file mode 100644
index 0000000..2b2a8e2
--- /dev/null
+++ b/src/extension/menu-extension-loader.h
@@ -0,0 +1,60 @@
+/*
+ * 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 .
+ *
+ */
+
+#ifndef UKUI_MENU_MENU_EXTENSION_LOADER_H
+#define UKUI_MENU_MENU_EXTENSION_LOADER_H
+
+#include
+#include
+
+#include "widget-extension.h"
+#include "context-menu-extension.h"
+
+namespace UkuiMenu {
+
+class MenuExtensionPlugin;
+
+class MenuExtensionLoader
+{
+public:
+ static MenuExtensionLoader *instance();
+
+ QList widgets() const;
+ QList menus() const;
+
+private:
+ MenuExtensionLoader();
+ void loadInternalExtension();
+ void loadExtensionFromDisk();
+
+ void setBlackList(const QStringList &blackList);
+ void load();
+ void expand();
+
+ void registerExtension(MenuExtensionPlugin *plugin);
+
+private:
+ QStringList m_blackList;
+ QList m_widgets;
+ QList m_menus;
+ static QHash plugins;
+};
+
+} // UkuiMenu
+
+#endif //UKUI_MENU_MENU_EXTENSION_LOADER_H
diff --git a/src/extension/menu-extension-plugin.cpp b/src/extension/menu-extension-plugin.cpp
new file mode 100644
index 0000000..799b9d7
--- /dev/null
+++ b/src/extension/menu-extension-plugin.cpp
@@ -0,0 +1,26 @@
+/*
+ * 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 .
+ *
+ */
+
+#include "menu-extension-plugin.h"
+
+UkuiMenu::MenuExtensionPlugin::MenuExtensionPlugin(QObject *parent) : QObject(parent)
+{
+
+}
+
+UkuiMenu::MenuExtensionPlugin::~MenuExtensionPlugin() = default;
diff --git a/src/extension/menu-extension-plugin.h b/src/extension/menu-extension-plugin.h
new file mode 100644
index 0000000..63cc9a5
--- /dev/null
+++ b/src/extension/menu-extension-plugin.h
@@ -0,0 +1,62 @@
+/*
+ * 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 .
+ *
+ */
+
+#ifndef UKUI_MENU_MENU_EXTENSION_PLUGIN_H
+#define UKUI_MENU_MENU_EXTENSION_PLUGIN_H
+
+#define UKUI_MENU_EXTENSION_I_FACE_TYPE "UKUI_MENU_EXTENSION"
+#define UKUI_MENU_EXTENSION_I_FACE_IID "org.ukui.menu.extension"
+#define UKUI_MENU_EXTENSION_I_FACE_VERSION "1.0.2"
+
+#include
+
+namespace UkuiMenu {
+
+class WidgetExtension;
+class ContextMenuExtension;
+
+class Q_DECL_EXPORT MenuExtensionPlugin : public QObject
+{
+public:
+ explicit MenuExtensionPlugin(QObject *parent = nullptr);
+ ~MenuExtensionPlugin() override;
+
+ /**
+ * 插件的唯一id,会被用于区分插件
+ * @return 唯一id
+ */
+ virtual QString id() = 0;
+
+ /**
+ * 创建一个Widget扩展
+ * @return 返回nullptr代表不生产此插件
+ */
+ virtual WidgetExtension *createWidgetExtension() = 0;
+
+ /**
+ * 创建上下文菜单扩展
+ * @return 返回nullptr代表不生产此插件
+ */
+ virtual ContextMenuExtension *createContextMenuExtension() = 0;
+};
+
+} // UkuiMenu
+
+Q_DECLARE_INTERFACE(UkuiMenu::MenuExtensionPlugin, UKUI_MENU_EXTENSION_I_FACE_IID)
+
+#endif //UKUI_MENU_MENU_EXTENSION_PLUGIN_H
diff --git a/src/extension/widget-extension-model.cpp b/src/extension/widget-extension-model.cpp
new file mode 100644
index 0000000..37f6344
--- /dev/null
+++ b/src/extension/widget-extension-model.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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 .
+ *
+ */
+
+#include "widget-extension-model.h"
+#include "menu-extension-loader.h"
+
+#include
+
+namespace UkuiMenu {
+
+WidgetExtensionModel::WidgetExtensionModel(QObject *parent) : QAbstractListModel(parent)
+{
+ m_widgets = MenuExtensionLoader::instance()->widgets();
+
+ for (int i = 0; i < m_widgets.size(); ++i) {
+ connect(m_widgets.at(i), &WidgetExtension::dataUpdated, this, [i, this] {
+ Q_EMIT dataChanged(QAbstractListModel::index(i), QAbstractListModel::index(i), {WidgetMetadata::Data});
+ });
+ }
+}
+
+QModelIndex WidgetExtensionModel::parent(const QModelIndex &child) const
+{
+ return {};
+}
+
+int WidgetExtensionModel::rowCount(const QModelIndex &parent) const
+{
+ return m_widgets.count();
+}
+
+int WidgetExtensionModel::columnCount(const QModelIndex &parent) const
+{
+ return 1;
+}
+
+QVariant WidgetExtensionModel::data(const QModelIndex &index, int role) const
+{
+ if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)) {
+ return {};
+ }
+
+ int row = index.row();
+ WidgetExtension *widget = m_widgets.at(row);
+
+ auto key = WidgetMetadata::Key(role);
+ switch (key) {
+ case WidgetMetadata::Id:
+ case WidgetMetadata::Icon:
+ case WidgetMetadata::Name:
+ case WidgetMetadata::Tooltip:
+ case WidgetMetadata::Version:
+ case WidgetMetadata::Description:
+ case WidgetMetadata::Main:
+ case WidgetMetadata::Type:
+ case WidgetMetadata::Flag:
+ return widget->metadata().value(key, {});
+ case WidgetMetadata::Data:
+ return widget->data();
+ default:
+ break;
+ }
+
+ return {};
+}
+
+QHash WidgetExtensionModel::roleNames() const
+{
+ QHash hash;
+
+ hash.insert(WidgetMetadata::Id, "id");
+ hash.insert(WidgetMetadata::Icon, "icon");
+ hash.insert(WidgetMetadata::Name, "name");
+ hash.insert(WidgetMetadata::Tooltip, "tooltip");
+ hash.insert(WidgetMetadata::Version, "version");
+ hash.insert(WidgetMetadata::Description, "description");
+ hash.insert(WidgetMetadata::Main, "main");
+ hash.insert(WidgetMetadata::Type, "type");
+ hash.insert(WidgetMetadata::Flag, "flag");
+ hash.insert(WidgetMetadata::Data, "data");
+
+ return hash;
+}
+
+WidgetExtension *WidgetExtensionModel::widgetAt(int index) const
+{
+ if (index < 0 || index >= m_widgets.count()) {
+ return nullptr;
+ }
+
+ return m_widgets.at(index);
+}
+
+void WidgetExtensionModel::notify(int index, const QVariantMap &data) const
+{
+ WidgetExtension *widget = widgetAt(index);
+ if (widget) {
+ widget->receive(data);
+ }
+}
+
+WidgetExtensionModel *WidgetExtensionModel::instance()
+{
+ static WidgetExtensionModel model;
+ return &model;
+}
+
+} // UkuiMenu
diff --git a/src/extension/widget-extension-model.h b/src/extension/widget-extension-model.h
new file mode 100644
index 0000000..a24c0d4
--- /dev/null
+++ b/src/extension/widget-extension-model.h
@@ -0,0 +1,50 @@
+/*
+ * 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 .
+ *
+ */
+
+#ifndef UKUI_MENU_WIDGET_EXTENSION_MODEL_H
+#define UKUI_MENU_WIDGET_EXTENSION_MODEL_H
+
+#include
+
+namespace UkuiMenu {
+
+class WidgetExtension;
+
+class WidgetExtensionModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ static WidgetExtensionModel *instance();
+ explicit WidgetExtensionModel(QObject *parent = nullptr);
+
+ QModelIndex parent(const QModelIndex &child) const override;
+ int rowCount(const QModelIndex &parent) const override;
+ int columnCount(const QModelIndex &parent) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ QHash roleNames() const override;
+
+ Q_INVOKABLE UkuiMenu::WidgetExtension *widgetAt(int index) const;
+ Q_INVOKABLE void notify(int index, const QVariantMap &data) const;
+
+private:
+ QList m_widgets;
+};
+
+} // UkuiMenu
+
+#endif //UKUI_MENU_WIDGET_EXTENSION_MODEL_H
diff --git a/src/extension/widget-extension.cpp b/src/extension/widget-extension.cpp
new file mode 100644
index 0000000..89e3893
--- /dev/null
+++ b/src/extension/widget-extension.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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 .
+ *
+ */
+
+#include "widget-extension.h"
+
+namespace UkuiMenu {
+
+WidgetExtension::WidgetExtension(QObject *parent) : QObject(parent)
+{
+ qRegisterMetaType();
+}
+
+int WidgetExtension::index() const
+{
+ return -1;
+}
+
+QVariantMap WidgetExtension::data()
+{
+ return {};
+}
+
+void WidgetExtension::receive(const QVariantMap &data)
+{
+ Q_UNUSED(data)
+}
+
+} // UkuiMenu
diff --git a/src/extension/widget-extension.h b/src/extension/widget-extension.h
new file mode 100644
index 0000000..ae909f0
--- /dev/null
+++ b/src/extension/widget-extension.h
@@ -0,0 +1,88 @@
+/*
+ * 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 .
+ *
+ */
+
+#ifndef UKUI_MENU_WIDGET_EXTENSION_H
+#define UKUI_MENU_WIDGET_EXTENSION_H
+
+#include
+#include
+
+namespace UkuiMenu {
+
+class WidgetMetadata
+{
+ Q_GADGET
+public:
+ enum Key {
+ Id = 0,
+ Icon,
+ Name,
+ Tooltip,
+ Version,
+ Description,
+ Main,
+ Type,
+ Flag,
+ Data
+ };
+ Q_ENUM(Key)
+
+ enum TypeValue {
+ Widget = 0x01, /**> 显示在插件区域 */
+ Button = 0x02, /**> 显示在侧边栏 */
+ AppList = 0x04 /**> 显示在应用列表,默认会显示在全屏界面 */
+ };
+ Q_ENUM(TypeValue)
+ Q_DECLARE_FLAGS(Types, TypeValue)
+ Q_FLAGS(Types)
+
+ enum FlagValue {
+ OnlySmallScreen = 0x01,
+ OnlyFullScreen = 0x02,
+ Normal = OnlySmallScreen | OnlyFullScreen
+ };
+ Q_ENUM(FlagValue)
+ Q_DECLARE_FLAGS(Flags, FlagValue)
+ Q_FLAGS(Flags)
+};
+
+typedef QMap MetadataMap;
+
+class WidgetExtension : public QObject
+{
+ Q_OBJECT
+public:
+ explicit WidgetExtension(QObject *parent = nullptr);
+ virtual int index() const;
+ virtual MetadataMap metadata() const = 0;
+
+ // 兼容老版本
+ virtual QVariantMap data();
+ virtual void receive(const QVariantMap &data);
+
+Q_SIGNALS:
+ void dataUpdated();
+};
+
+} // UkuiMenu
+
+Q_DECLARE_METATYPE(UkuiMenu::MetadataMap)
+Q_DECLARE_METATYPE(UkuiMenu::WidgetMetadata::TypeValue)
+Q_DECLARE_METATYPE(UkuiMenu::WidgetMetadata::FlagValue)
+
+#endif //UKUI_MENU_WIDGET_EXTENSION_H
diff --git a/src/extension/widget-model.cpp b/src/extension/widget-model.cpp
new file mode 100644
index 0000000..b73f997
--- /dev/null
+++ b/src/extension/widget-model.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 .
+ *
+ */
+
+#include "widget-model.h"
+#include "widget-extension-model.h"
+#include
+#include
+
+namespace UkuiMenu {
+
+// ====== WidgetModel ====== //
+WidgetModel::WidgetModel(QObject *parent) : QSortFilterProxyModel(parent)
+{
+ init();
+}
+
+WidgetMetadata::Types WidgetModel::types() const
+{
+ return m_types;
+}
+
+void WidgetModel::setTypes(WidgetMetadata::Types types)
+{
+ if (m_types == types) {
+ return;
+ }
+
+ m_types = types;
+ invalidateFilter();
+}
+
+WidgetMetadata::Flags WidgetModel::flags() const
+{
+ return m_flags;
+}
+
+void WidgetModel::setFlags(WidgetMetadata::Flags flags)
+{
+ if (m_flags == flags) {
+ return;
+ }
+
+ m_flags = flags;
+ invalidateFilter();
+}
+
+bool WidgetModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
+{
+ QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
+
+ bool acceptFlag = m_types.testFlag(index.data(WidgetMetadata::Type).value());
+ if (acceptFlag) {
+ return m_flags.testFlag(index.data(WidgetMetadata::Flag).value());
+ }
+ return false;
+}
+
+void WidgetModel::init()
+{
+ QSortFilterProxyModel::setSourceModel(WidgetExtensionModel::instance());
+// invalidateFilter();
+}
+
+void WidgetModel::send(int index, const QVariantMap &data)
+{
+ auto sourceModel = qobject_cast(QSortFilterProxyModel::sourceModel());
+ if (sourceModel) {
+ sourceModel->notify(index, data);
+ }
+}
+
+} // UkuiMenu
diff --git a/src/extension/widget-model.h b/src/extension/widget-model.h
new file mode 100644
index 0000000..d5d3cf7
--- /dev/null
+++ b/src/extension/widget-model.h
@@ -0,0 +1,54 @@
+/*
+ * 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 .
+ *
+ */
+
+#ifndef UKUI_MENU_WIDGET_MODEL_H
+#define UKUI_MENU_WIDGET_MODEL_H
+
+#include
+#include
+#include "widget-extension.h"
+
+namespace UkuiMenu {
+
+class WidgetModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+ Q_PROPERTY(UkuiMenu::WidgetMetadata::Types types READ types WRITE setTypes)
+ Q_PROPERTY(UkuiMenu::WidgetMetadata::Flags flags READ flags WRITE setFlags)
+public:
+ explicit WidgetModel(QObject *parent = nullptr);
+
+ Q_INVOKABLE void init();
+ Q_INVOKABLE void send(int index, const QVariantMap &data);
+
+ WidgetMetadata::Types types() const;
+ void setTypes(WidgetMetadata::Types types);
+ WidgetMetadata::Flags flags() const;
+ void setFlags(WidgetMetadata::Flags flags);
+
+protected:
+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
+
+private:
+ WidgetMetadata::Types m_types = WidgetMetadata::Widget;
+ WidgetMetadata::Flags m_flags = WidgetMetadata::Normal;
+};
+
+}
+
+#endif //UKUI_MENU_WIDGET_MODEL_H
diff --git a/src/ukui-menu-application.cpp b/src/ukui-menu-application.cpp
index 74bdfc0..9182a9e 100644
--- a/src/ukui-menu-application.cpp
+++ b/src/ukui-menu-application.cpp
@@ -33,6 +33,7 @@
#include "data-provider-manager.h"
#include "event-track.h"
#include "sidebar-button-utils.h"
+#include "extension/widget-model.h"
#include
#include
@@ -67,6 +68,8 @@ void UkuiMenuApplication::registerQmlTypes()
ModelManager::registerMetaTypes();
+ qmlRegisterType(uri, versionMajor, versionMinor, "WidgetModel");
+
// commons
qRegisterMetaType("DataType::Type");
qRegisterMetaType("DataEntity");