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");