From 4a2847f03fdd6bdc71a29826e773c5fcb9af9334 Mon Sep 17 00:00:00 2001 From: donghualin Date: Thu, 12 Jan 2023 13:09:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8A=A0=E8=BD=BD=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E7=A7=BB=E5=8A=A8=E5=88=B0=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E9=87=8C=E5=8D=95=E7=8B=AC=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将加载插件的流程和快捷面板的功能移动到单独的插件中,精简任务栏的代码 Log: Influence: 加载插件 Task: https://pms.uniontech.com/task-view-222353.html Change-Id: I9b2fbe4f32c852f6a3535daab87c63741bd8914a --- debian/dde-dock.install | 1 + frame/CMakeLists.txt | 2 +- interfaces/pluginmanagerinterface.h | 44 ++ plugins/CMakeLists.txt | 2 +- plugins/pluginmanager/CMakeLists.txt | 45 ++ .../pluginmanager/dockplugincontroller.cpp | 704 ++++++++++++++++++ plugins/pluginmanager/dockplugincontroller.h | 116 +++ plugins/pluginmanager/iconmanager.cpp | 122 +++ plugins/pluginmanager/iconmanager.h | 52 ++ plugins/pluginmanager/largerquickitem.cpp | 313 ++++++++ plugins/pluginmanager/largerquickitem.h | 82 ++ plugins/pluginmanager/linequickitem.cpp | 105 +++ plugins/pluginmanager/linequickitem.h | 58 ++ .../pluginadapter/pluginadapter.cpp | 209 ++++++ .../pluginadapter/pluginadapter.h | 81 ++ .../pluginadapter/pluginsiteminterface_v20.h | 261 +++++++ plugins/pluginmanager/pluginchildpage.cpp | 146 ++++ plugins/pluginmanager/pluginchildpage.h | 61 ++ plugins/pluginmanager/pluginmanager.cpp | 195 +++++ plugins/pluginmanager/pluginmanager.h | 72 ++ plugins/pluginmanager/pluginmanager.json | 4 + plugins/pluginmanager/pluginmanager.qrc | 5 + .../pluginmanager/quicksettingcontainer.cpp | 415 +++++++++++ plugins/pluginmanager/quicksettingcontainer.h | 94 +++ plugins/pluginmanager/quicksettingitem.cpp | 148 ++++ plugins/pluginmanager/quicksettingitem.h | 69 ++ .../pluginmanager/resources/dock_control.dci | Bin 0 -> 767 bytes plugins/pluginmanager/standardquickitem.cpp | 200 +++++ plugins/pluginmanager/standardquickitem.h | 57 ++ 29 files changed, 3661 insertions(+), 2 deletions(-) create mode 100644 interfaces/pluginmanagerinterface.h create mode 100644 plugins/pluginmanager/CMakeLists.txt create mode 100644 plugins/pluginmanager/dockplugincontroller.cpp create mode 100644 plugins/pluginmanager/dockplugincontroller.h create mode 100644 plugins/pluginmanager/iconmanager.cpp create mode 100644 plugins/pluginmanager/iconmanager.h create mode 100644 plugins/pluginmanager/largerquickitem.cpp create mode 100644 plugins/pluginmanager/largerquickitem.h create mode 100644 plugins/pluginmanager/linequickitem.cpp create mode 100644 plugins/pluginmanager/linequickitem.h create mode 100644 plugins/pluginmanager/pluginadapter/pluginadapter.cpp create mode 100644 plugins/pluginmanager/pluginadapter/pluginadapter.h create mode 100644 plugins/pluginmanager/pluginadapter/pluginsiteminterface_v20.h create mode 100644 plugins/pluginmanager/pluginchildpage.cpp create mode 100644 plugins/pluginmanager/pluginchildpage.h create mode 100644 plugins/pluginmanager/pluginmanager.cpp create mode 100644 plugins/pluginmanager/pluginmanager.h create mode 100644 plugins/pluginmanager/pluginmanager.json create mode 100644 plugins/pluginmanager/pluginmanager.qrc create mode 100644 plugins/pluginmanager/quicksettingcontainer.cpp create mode 100644 plugins/pluginmanager/quicksettingcontainer.h create mode 100644 plugins/pluginmanager/quicksettingitem.cpp create mode 100644 plugins/pluginmanager/quicksettingitem.h create mode 100644 plugins/pluginmanager/resources/dock_control.dci create mode 100644 plugins/pluginmanager/standardquickitem.cpp create mode 100644 plugins/pluginmanager/standardquickitem.h diff --git a/debian/dde-dock.install b/debian/dde-dock.install index 4ea0c2294..f10faa542 100644 --- a/debian/dde-dock.install +++ b/debian/dde-dock.install @@ -1,6 +1,7 @@ usr/share usr/bin etc/dde-dock +usr/lib/dde-dock/plugins/loader/libpluginmanager.so usr/lib/dde-dock/plugins/libshutdown.so usr/lib/dde-dock/plugins/libtrash.so usr/lib/dde-dock/plugins/liboverlay-warning.so diff --git a/frame/CMakeLists.txt b/frame/CMakeLists.txt index 949a9dbdf..dc4b4742a 100644 --- a/frame/CMakeLists.txt +++ b/frame/CMakeLists.txt @@ -12,7 +12,7 @@ endif() generation_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/dbusinterface/xml ${CMAKE_CURRENT_SOURCE_DIR}/dbusinterface/generation_dbus_interface) # Sources files -file(GLOB_RECURSE SRCS "*.h" "*.cpp" "../widgets/*.h" "../widgets/*.cpp") +file(GLOB_RECURSE SRCS "*.h" "*.cpp" "../widgets/*.h" "../widgets/*.cpp" "../interfaces/*.h") # Find the library find_package(PkgConfig REQUIRED) diff --git a/interfaces/pluginmanagerinterface.h b/interfaces/pluginmanagerinterface.h new file mode 100644 index 000000000..d20e8fd7b --- /dev/null +++ b/interfaces/pluginmanagerinterface.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 ~ 2023 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 PLUGINMANAGERINTERFACE_H +#define PLUGINMANAGERINTERFACE_H + +#include +#include + +class PluginsItemInterface; + +class PluginManagerInterface : public QObject +{ + Q_OBJECT + +public: + virtual QList plugins() const = 0; + virtual QList pluginsInSetting() const = 0; + virtual QList currentPlugins() const = 0; + virtual QString itemKey(PluginsItemInterface *itemInter) const = 0; + virtual QJsonObject metaData(PluginsItemInterface *itemInter) const = 0; + +Q_SIGNALS: + void pluginLoadFinished(); +}; + +#endif // PLUGINMANAGERINTERFACE_H diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 7f830eb24..bb7858de3 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -4,7 +4,7 @@ add_subdirectory("power") add_subdirectory("sound") add_subdirectory("display") add_subdirectory("media") -#add_subdirectory("tray") +add_subdirectory("pluginmanager") add_subdirectory("trash") add_subdirectory("keyboard-layout") add_subdirectory("onboard") diff --git a/plugins/pluginmanager/CMakeLists.txt b/plugins/pluginmanager/CMakeLists.txt new file mode 100644 index 000000000..1a7d5ab00 --- /dev/null +++ b/plugins/pluginmanager/CMakeLists.txt @@ -0,0 +1,45 @@ + +set(PLUGIN_NAME "pluginmanager") + +project(${PLUGIN_NAME}) + +# Sources files +file(GLOB_RECURSE SRCS "*.h" "*.cpp" "*.qrc" "../../frame/drag/quickdragcore.h" "../../frame/drag/quickdragcore.cpp" +"../../frame/util/settingconfig.h" "../../frame/util/settingconfig.cpp" +"../../frame/util/pluginloader.h" "../../frame/util/pluginloader.cpp" +"../../frame/dbus/dockinterface.h" "../../frame/dbus/dockinterface.cpp" +"../../frame/dbusinterface/generation_dbus_interface/org_deepin_dde_daemon_dock1.h" +"../../frame/dbusinterface/generation_dbus_interface/org_deepin_dde_daemon_dock1.cpp" +"../../frame/dbusinterface/types/dockrect.h" +"../../frame/dbusinterface/types/dockrect.cpp" +"../../interfaces/pluginmanagerinterface.h" +) + +find_package(PkgConfig REQUIRED) +find_package(Qt5Widgets REQUIRED) +find_package(Qt5Svg REQUIRED) +find_package(Qt5DBus REQUIRED) +find_package(DtkWidget REQUIRED) +pkg_check_modules(QGSettings REQUIRED gsettings-qt) + +add_library(${PLUGIN_NAME} SHARED ${SRCS}) +set_target_properties(${PLUGIN_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ../loader/) +target_include_directories(${PLUGIN_NAME} PUBLIC ${DtkWidget_INCLUDE_DIRS} + ${Qt5DBus_INCLUDE_DIRS} + ${QGSettings_INCLUDE_DIRS} + ./pluginadapter + ../../frame/drag + ../../frame/dbusinterface + ../../frame/dbusinterface/generation_dbus_interface/ + ../../interfaces + ) + +target_link_libraries(${PLUGIN_NAME} PRIVATE + ${DtkWidget_LIBRARIES} + ${Qt5Widgets_LIBRARIES} + ${Qt5Svg_LIBRARIES} + ${Qt5DBus_LIBRARIES} + ${QGSettings_LIBRARIES} +) + +install(TARGETS ${PLUGIN_NAME} LIBRARY DESTINATION lib/dde-dock/plugins/loader) diff --git a/plugins/pluginmanager/dockplugincontroller.cpp b/plugins/pluginmanager/dockplugincontroller.cpp new file mode 100644 index 000000000..cdb0b5a94 --- /dev/null +++ b/plugins/pluginmanager/dockplugincontroller.cpp @@ -0,0 +1,704 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 "dockplugincontroller.h" +#include "pluginsiteminterface.h" +#include "pluginsiteminterface_v20.h" +#include "pluginadapter.h" +#include "utils.h" +#include "settingconfig.h" + +#include +#include + +#include +#include +#include +#include + +#define PLUGININFO "pluginInfo" +#define DOCK_QUICK_PLUGINS "Dock_Quick_Plugins" + +static const QStringList CompatiblePluginApiList { + "1.1.1", + "1.2", + "1.2.1", + "1.2.2", + DOCK_PLUGIN_API_VERSION +}; + +class PluginInfo : public QObject +{ +public: + PluginInfo() : QObject(nullptr), m_loaded(false), m_visible(false) {} + bool m_loaded; + bool m_visible; + QString m_itemKey; +}; + +DockPluginController::DockPluginController(PluginProxyInterface *proxyInter, QObject *parent) + : QObject(parent) + , m_dbusDaemonInterface(QDBusConnection::sessionBus().interface()) + , m_dockDaemonInter(new DockInter(dockServiceName(), dockServicePath(), QDBusConnection::sessionBus(), this)) + , m_proxyInter(proxyInter) +{ + qApp->installEventFilter(this); + + refreshPluginSettings(); + + connect(SETTINGCONFIG, &SettingConfig::valueChanged, this, &DockPluginController::onConfigChanged); + connect(m_dockDaemonInter, &DockInter::PluginSettingsSynced, this, &DockPluginController::refreshPluginSettings, Qt::QueuedConnection); +} + +DockPluginController::~DockPluginController() +{ + for (auto inter : m_pluginsMap.keys()) { + delete m_pluginsMap.value(inter).value("pluginloader"); + m_pluginsMap[inter]["pluginloader"] = nullptr; + if (m_pluginsMap[inter].contains(PLUGININFO)) + m_pluginsMap[inter][PLUGININFO]->deleteLater(); + m_pluginsMap.remove(inter); + delete inter; + inter = nullptr; + } +} + +QList DockPluginController::plugins() const +{ + return m_pluginsMap.keys(); +} + +QList DockPluginController::pluginsInSetting() const +{ + // 插件有三种状态 + // 1、所有的插件,不管这个插件是否调用itemAdded方法,只要是通过dock加载的插件(换句话说,也就是在/lib/dde-dock/plugins目录下的插件) + // 2、在1的基础上,插件自身调用了itemAdded方法的插件,例如机器上没有蓝牙设备,那么蓝牙插件就不会调用itemAdded方法,这时候就不算 + // 3、在2的基础上,由控制中心来决定那些插件是否在任务栏显示的插件 + // 此处返回的是第二种插件 + QList settingPlugins; + QMap pluginSort; + for (auto it = m_pluginsMap.begin(); it != m_pluginsMap.end(); it++) { + PluginsItemInterface *plugin = it.key(); + qInfo() << plugin->pluginName(); + if (plugin->pluginDisplayName().isEmpty()) + continue; + + QMap pluginMap = it.value(); + // 如果不包含PLUGININFO这个key值,肯定是未加载 + if (!pluginMap.contains(PLUGININFO)) + continue; + + PluginInfo *pluginInfo = static_cast(pluginMap[PLUGININFO]); + if (!pluginInfo->m_loaded) + continue; + + // 这里只需要返回插件为可以在控制中心设置的插件 + if (!(plugin->flags() & PluginFlag::Attribute_CanSetting)) + continue; + + settingPlugins << plugin; + pluginSort[plugin] = plugin->itemSortKey(pluginInfo->m_itemKey); + } + + std::sort(settingPlugins.begin(), settingPlugins.end(), [ pluginSort ](PluginsItemInterface *plugin1, PluginsItemInterface *plugin2) { + return pluginSort[plugin1] < pluginSort[plugin2]; + }); + + return settingPlugins; +} + +QList DockPluginController::currentPlugins() const +{ + QList loadedPlugins; + + QMap pluginSortMap; + for (auto it = m_pluginsMap.begin(); it != m_pluginsMap.end(); it++) { + QMap objectMap = it.value(); + if (!objectMap.contains(PLUGININFO)) + continue; + + PluginInfo *pluginInfo = static_cast(objectMap[PLUGININFO]); + if (!pluginInfo->m_loaded) + continue; + + PluginsItemInterface *plugin = it.key(); + loadedPlugins << plugin; + pluginSortMap[plugin] = plugin->itemSortKey(pluginInfo->m_itemKey); + } + + std::sort(loadedPlugins.begin(), loadedPlugins.end(), [ pluginSortMap ](PluginsItemInterface *pluginItem1, PluginsItemInterface *pluginItem2) { + return pluginSortMap.value(pluginItem1) < pluginSortMap.value(pluginItem2); + }); + return loadedPlugins; +} + +void DockPluginController::saveValue(PluginsItemInterface *const itemInter, const QString &key, const QVariant &value) +{ + savePluginValue(getPluginInterface(itemInter), key, value); +} + +const QVariant DockPluginController::getValue(PluginsItemInterface *const itemInter, const QString &key, const QVariant &fallback) +{ + return getPluginValue(getPluginInterface(itemInter), key, fallback); +} + +void DockPluginController::removeValue(PluginsItemInterface *const itemInter, const QStringList &keyList) +{ + removePluginValue(getPluginInterface(itemInter), keyList); +} + +void DockPluginController::itemAdded(PluginsItemInterface * const itemInter, const QString &itemKey) +{ + PluginsItemInterface *pluginItem = getPluginInterface(itemInter); + + PluginAdapter *pluginAdapter = dynamic_cast(pluginItem); + if (pluginAdapter) { + // 如果该插件可以正常转换为PluginAdapter插件,表示当前插件是v20插件,为了兼容v20插件 + // 中获取ICON,因此,使用调用插件的itemWidget来截图的方式返回QIcon,所以此处传入itemKey + pluginAdapter->setItemKey(itemKey); + } + + // 如果是通过插件来调用m_proxyInter的 + PluginInfo *pluginInfo = nullptr; + QMap &interfaceData = m_pluginsMap[pluginItem]; + if (interfaceData.contains(PLUGININFO)) { + pluginInfo = static_cast(interfaceData[PLUGININFO]); + // 如果插件已经加载,则无需再次加载(此处保证插件出现重复调用itemAdded的情况) + if (pluginInfo->m_loaded) + return; + } else { + pluginInfo = new PluginInfo; + interfaceData[PLUGININFO] = pluginInfo; + } + pluginInfo->m_itemKey = itemKey; + pluginInfo->m_loaded = true; + + if (pluginCanDock(pluginItem)) + addPluginItem(pluginItem, itemKey); + + Q_EMIT pluginInserted(pluginItem, itemKey); +} + +void DockPluginController::itemUpdate(PluginsItemInterface * const itemInter, const QString &itemKey) +{ + m_proxyInter->itemUpdate(getPluginInterface(itemInter), itemKey); +} + +void DockPluginController::itemRemoved(PluginsItemInterface * const itemInter, const QString &itemKey) +{ + PluginsItemInterface *pluginInter = getPluginInterface(itemInter); + // 更新字段中的isLoaded字段,表示当前没有加载 + QMap &interfaceData = m_pluginsMap[pluginInter]; + if (interfaceData.contains(PLUGININFO)) { + PluginInfo *pluginInfo = static_cast(interfaceData[PLUGININFO]); + // 将是否加载的标记修改为未加载 + pluginInfo->m_loaded = false; + } + + removePluginItem(pluginInter, itemKey); + Q_EMIT pluginRemoved(pluginInter); +} + +void DockPluginController::requestWindowAutoHide(PluginsItemInterface * const itemInter, const QString &itemKey, const bool autoHide) +{ + m_proxyInter->requestWindowAutoHide(getPluginInterface(itemInter), itemKey, autoHide); +} + +void DockPluginController::requestRefreshWindowVisible(PluginsItemInterface * const itemInter, const QString &itemKey) +{ + m_proxyInter->requestRefreshWindowVisible(getPluginInterface(itemInter), itemKey); +} + +// 请求页面显示或者隐藏,由插件内部来调用,例如在移除蓝牙插件后,如果已经弹出了蓝牙插件的面板,则隐藏面板 +void DockPluginController::requestSetAppletVisible(PluginsItemInterface * const itemInter, const QString &itemKey, const bool visible) +{ + PluginsItemInterface *pluginInter = getPluginInterface(itemInter); + Q_EMIT requestAppletVisible(pluginInter, itemKey, visible); + m_proxyInter->requestSetAppletVisible(pluginInter, itemKey, visible); +} + +PluginsItemInterface *DockPluginController::getPluginInterface(PluginsItemInterface * const itemInter) +{ + // 先从事先定义好的map中查找,如果没有找到,就是v23插件,直接返回当前插件的指针 + qulonglong pluginAddr = (qulonglong)itemInter; + if (m_pluginAdapterMap.contains(pluginAddr)) + return m_pluginAdapterMap[pluginAddr]; + + return itemInter; +} + +void DockPluginController::addPluginItem(PluginsItemInterface * const itemInter, const QString &itemKey) +{ + // 如果这个插件都没有加载,那么此处肯定是无需新增 + if (!m_pluginsMap.contains(itemInter)) + return; + + PluginInfo *pluginInfo = nullptr; + QMap &interfaceData = m_pluginsMap[itemInter]; + // 此处的PLUGININFO的数据已经在前面调用的地方给填充了数据,如果没有获取到这个数据,则无需新增 + if (!interfaceData.contains(PLUGININFO)) + return; + + pluginInfo = static_cast(interfaceData[PLUGININFO]); + pluginInfo->m_visible = true; + + m_proxyInter->itemAdded(itemInter, itemKey); +} + +void DockPluginController::removePluginItem(PluginsItemInterface * const itemInter, const QString &itemKey) +{ + if (!m_pluginsMap.contains(itemInter)) + return; + + // 更新字段中的isLoaded字段,表示当前没有加载 + QMap &interfaceData = m_pluginsMap[itemInter]; + if (!interfaceData.contains(PLUGININFO)) + return; + + PluginInfo *pluginInfo = static_cast(interfaceData[PLUGININFO]); + // 将是否在任务栏显示的标记改为不显示 + pluginInfo->m_visible = false; + m_proxyInter->itemRemoved(itemInter, itemKey); +} + +QString DockPluginController::itemKey(PluginsItemInterface *itemInter) const +{ + if (!m_pluginsMap.contains(itemInter)) + return QString(); + + QMap interfaceData = m_pluginsMap[itemInter]; + if (!interfaceData.contains(PLUGININFO)) + return QString(); + + PluginInfo *pluginInfo = static_cast(interfaceData[PLUGININFO]); + return pluginInfo->m_itemKey; +} + +QJsonObject DockPluginController::metaData(PluginsItemInterface *pluginItem) +{ + if (!m_pluginsMap.contains(pluginItem)) + return QJsonObject(); + + QPluginLoader *pluginLoader = qobject_cast(m_pluginsMap[pluginItem].value("pluginloader")); + + if (!pluginLoader) + return QJsonObject(); + + return pluginLoader->metaData().value("MetaData").toObject(); +} + +void DockPluginController::savePluginValue(PluginsItemInterface * const itemInter, const QString &key, const QVariant &value) +{ + // is it necessary? + // refreshPluginSettings(); + + // save to local cache + QJsonObject localObject = m_pluginSettingsObject.value(itemInter->pluginName()).toObject(); + localObject.insert(key, QJsonValue::fromVariant(value)); //Note: QVariant::toJsonValue() not work in Qt 5.7 + + // save to daemon + QJsonObject remoteObject, remoteObjectInter; + remoteObjectInter.insert(key, QJsonValue::fromVariant(value)); //Note: QVariant::toJsonValue() not work in Qt 5.7 + remoteObject.insert(itemInter->pluginName(), remoteObjectInter); + + if (itemInter->type() == PluginsItemInterface::Fixed && key == "enable" && !value.toBool()) { + int fixedPluginCount = 0; + // 遍历FixPlugin插件个数 + for (auto it(m_pluginsMap.begin()); it != m_pluginsMap.end();) { + if (it.key()->type() == PluginsItemInterface::Fixed) { + fixedPluginCount++; + } + ++it; + } + // 修改插件的order值,位置为队尾 + QString name = localObject.keys().last(); + // 此次做一下判断,有可能初始数据不存在pos_*字段,会导致enable字段被修改。或者此处可以循环所有字段是否存在pos_开头的字段? + if (name != key) { + localObject.insert(name, QJsonValue::fromVariant(fixedPluginCount)); //Note: QVariant::toJsonValue() not work in Qt 5.7 + // daemon中同样修改 + remoteObjectInter.insert(name, QJsonValue::fromVariant(fixedPluginCount)); //Note: QVariant::toJsonValue() not work in Qt 5.7 + remoteObject.insert(itemInter->pluginName(), remoteObjectInter); + } + } + + m_pluginSettingsObject.insert(itemInter->pluginName(), localObject); + m_dockDaemonInter->MergePluginSettings(QJsonDocument(remoteObject).toJson(QJsonDocument::JsonFormat::Compact)); +} + +const QVariant DockPluginController::getPluginValue(PluginsItemInterface * const itemInter, const QString &key, const QVariant &fallback) +{ + // load from local cache + QVariant v = m_pluginSettingsObject.value(itemInter->pluginName()).toObject().value(key).toVariant(); + if (v.isNull() || !v.isValid()) { + v = fallback; + } + + return v; +} + +void DockPluginController::removePluginValue(PluginsItemInterface * const itemInter, const QStringList &keyList) +{ + if (keyList.isEmpty()) { + m_pluginSettingsObject.remove(itemInter->pluginName()); + } else { + QJsonObject localObject = m_pluginSettingsObject.value(itemInter->pluginName()).toObject(); + for (auto key : keyList) { + localObject.remove(key); + } + m_pluginSettingsObject.insert(itemInter->pluginName(), localObject); + } + + m_dockDaemonInter->RemovePluginSettings(itemInter->pluginName(), keyList); +} + +void DockPluginController::startLoadPlugin(const QStringList &dirs) +{ + QDir dir; + for (const QString &path : dirs) { + if (!dir.exists(path)) + continue; + + startLoader(new PluginLoader(path, this)); + } +} + +bool DockPluginController::isPluginLoaded(PluginsItemInterface *itemInter) +{ + if (!m_pluginsMap.contains(itemInter)) + return false; + + QMap pluginObject = m_pluginsMap.value(itemInter); + if (!pluginObject.contains(PLUGININFO)) + return false; + + PluginInfo *pluginInfo = static_cast(pluginObject.value(PLUGININFO)); + return pluginInfo->m_visible; +} + +QObject *DockPluginController::pluginItemAt(PluginsItemInterface *const itemInter, const QString &itemKey) const +{ + if (!m_pluginsMap.contains(itemInter)) + return nullptr; + + return m_pluginsMap[itemInter][itemKey]; +} + +PluginsItemInterface *DockPluginController::pluginInterAt(const QString &itemKey) +{ + QMapIterator> it(m_pluginsMap); + while (it.hasNext()) { + it.next(); + if (it.value().keys().contains(itemKey)) { + return it.key(); + } + } + + return nullptr; +} + +PluginsItemInterface *DockPluginController::pluginInterAt(QObject *destItem) +{ + QMapIterator> it(m_pluginsMap); + while (it.hasNext()) { + it.next(); + if (it.value().values().contains(destItem)) { + return it.key(); + } + } + + return nullptr; +} + +void DockPluginController::startLoader(PluginLoader *loader) +{ + connect(loader, &PluginLoader::finished, loader, &PluginLoader::deleteLater, Qt::QueuedConnection); + connect(loader, &PluginLoader::pluginFounded, this, [ = ](const QString &pluginFile) { + QPair pair; + pair.first = pluginFile; + pair.second = nullptr; + m_pluginLoadMap.insert(pair, false); + }); + connect(loader, &PluginLoader::pluginFounded, this, &DockPluginController::loadPlugin, Qt::QueuedConnection); + + int delay = Utils::SettingValue("com.deepin.dde.dock", "/com/deepin/dde/dock/", "delay-plugins-time", 0).toInt(); + QTimer::singleShot(delay, loader, [ = ] { loader->start(QThread::LowestPriority); }); +} + +void DockPluginController::displayModeChanged() +{ + const Dock::DisplayMode displayMode = qApp->property(PROP_DISPLAY_MODE).value(); + const auto inters = m_pluginsMap.keys(); + + for (auto inter : inters) + inter->displayModeChanged(displayMode); +} + +void DockPluginController::positionChanged() +{ + const Dock::Position position = qApp->property(PROP_POSITION).value(); + const auto inters = m_pluginsMap.keys(); + + for (auto inter : inters) + inter->positionChanged(position); +} + +void DockPluginController::loadPlugin(const QString &pluginFile) +{ + QPluginLoader *pluginLoader = new QPluginLoader(pluginFile, this); + const QJsonObject &meta = pluginLoader->metaData().value("MetaData").toObject(); + const QString &pluginApi = meta.value("api").toString(); + bool pluginIsValid = true; + if (pluginApi.isEmpty() || !CompatiblePluginApiList.contains(pluginApi)) { + qDebug() << objectName() + << "plugin api version not matched! expect versions:" << CompatiblePluginApiList + << ", got version:" << pluginApi + << ", the plugin file is:" << pluginFile; + + pluginIsValid = false; + } + + PluginsItemInterface *interface = qobject_cast(pluginLoader->instance()); + if (!interface) { + // 如果识别当前插件失败,就认为这个插件是v20的插件,将其转换为v20插件接口 + PluginsItemInterface_V20 *interface_v20 = qobject_cast(pluginLoader->instance()); + if (interface_v20) { + // 将v20插件接口通过适配器转换成v23的接口,方便在后面识别 + PluginAdapter *pluginAdapter = new PluginAdapter(interface_v20, pluginLoader); + // 将适配器的地址保存到map列表中,因为适配器自己会调用itemAdded方法,转换成PluginsItemInterface类,但是实际上它 + // 对应的是PluginAdapter类,因此,这个map用于在后面的itemAdded方法中用来查找 + m_pluginAdapterMap[(qulonglong)(interface_v20)] = pluginAdapter; + interface = pluginAdapter; + } + } + + if (!interface) { + qDebug() << objectName() << "load plugin failed!!!" << pluginLoader->errorString() << pluginFile; + + pluginLoader->unload(); + pluginLoader->deleteLater(); + + pluginIsValid = false; + } + + if (!pluginIsValid) { + for (auto &pair : m_pluginLoadMap.keys()) { + if (pair.first == pluginFile) { + m_pluginLoadMap.remove(pair); + } + } + QString notifyMessage(tr("The plugin %1 is not compatible with the system.")); + Dtk::Core::DUtil::DNotifySender(notifyMessage.arg(QFileInfo(pluginFile).fileName())).appIcon("dialog-warning").call(); + return; + } + + if (interface->pluginName() == "multitasking" && (Utils::IS_WAYLAND_DISPLAY || Dtk::Core::DSysInfo::deepinType() == Dtk::Core::DSysInfo::DeepinServer)) { + for (auto &pair : m_pluginLoadMap.keys()) { + if (pair.first == pluginFile) { + m_pluginLoadMap.remove(pair); + } + } + return; + } + + QMapIterator, bool> it(m_pluginLoadMap); + while (it.hasNext()) { + it.next(); + if (it.key().first == pluginFile) { + m_pluginLoadMap.remove(it.key()); + QPair newPair; + newPair.first = pluginFile; + newPair.second = interface; + m_pluginLoadMap.insert(newPair, false); + break; + } + } + + // 保存 PluginLoader 对象指针 + QMap interfaceData; + interfaceData["pluginloader"] = pluginLoader; + m_pluginsMap.insert(interface, interfaceData); + QString dbusService = meta.value("depends-daemon-dbus-service").toString(); + if (!dbusService.isEmpty() && !m_dbusDaemonInterface->isServiceRegistered(dbusService).value()) { + qDebug() << objectName() << dbusService << "daemon has not started, waiting for signal"; + connect(m_dbusDaemonInterface, &QDBusConnectionInterface::serviceOwnerChanged, this, + [ = ](const QString & name, const QString & oldOwner, const QString & newOwner) { + Q_UNUSED(oldOwner); + if (name == dbusService && !newOwner.isEmpty()) { + qDebug() << objectName() << dbusService << "daemon started, init plugin and disconnect"; + initPlugin(interface); + disconnect(m_dbusDaemonInterface); + } + } + ); + return; + } + + // NOTE(justforlxz): 插件的所有初始化工作都在init函数中进行, + // loadPlugin函数是按队列执行的,initPlugin函数会有可能导致 + // 函数执行被阻塞。 + QTimer::singleShot(1, this, [ = ] { + initPlugin(interface); + }); +} + +void DockPluginController::initPlugin(PluginsItemInterface *interface) +{ + if (!interface) + return; + + qDebug() << objectName() << "init plugin: " << interface->pluginName(); + interface->init(this); + + for (const auto &pair : m_pluginLoadMap.keys()) { + if (pair.second == interface) + m_pluginLoadMap.insert(pair, true); + } + + bool loaded = true; + for (int i = 0; i < m_pluginLoadMap.keys().size(); ++i) { + if (!m_pluginLoadMap.values()[i]) { + loaded = false; + break; + } + } + + // 插件全部加载完成 + if (loaded) { + emit pluginLoadFinished(); + } + qDebug() << objectName() << "init plugin finished: " << interface->pluginName(); +} + +void DockPluginController::refreshPluginSettings() +{ + const QString &pluginSettings = m_dockDaemonInter->GetPluginSettings().value(); + if (pluginSettings.isEmpty()) { + qDebug() << "Error! get plugin settings from dbus failed!"; + return; + } + + const QJsonObject &pluginSettingsObject = QJsonDocument::fromJson(pluginSettings.toLocal8Bit()).object(); + if (pluginSettingsObject.isEmpty()) { + return; + } + + // nothing changed + if (pluginSettingsObject == m_pluginSettingsObject) { + return; + } + + for (auto pluginsIt = pluginSettingsObject.constBegin(); pluginsIt != pluginSettingsObject.constEnd(); ++pluginsIt) { + const QString &pluginName = pluginsIt.key(); + const QJsonObject &settingsObject = pluginsIt.value().toObject(); + QJsonObject newSettingsObject = m_pluginSettingsObject.value(pluginName).toObject(); + for (auto settingsIt = settingsObject.constBegin(); settingsIt != settingsObject.constEnd(); ++settingsIt) { + newSettingsObject.insert(settingsIt.key(), settingsIt.value()); + } + // TODO: remove not exists key-values + m_pluginSettingsObject.insert(pluginName, newSettingsObject); + } + + // not notify plugins to refresh settings if this update is not emit by dock daemon + if (sender() != m_dockDaemonInter) { + return; + } + + // notify all plugins to reload plugin settings + for (PluginsItemInterface *pluginInter : m_pluginsMap.keys()) { + pluginInter->pluginSettingsChanged(); + } + + // reload all plugin items for sort order or container + QMap> pluginsMapTemp = m_pluginsMap; + for (auto it = pluginsMapTemp.constBegin(); it != pluginsMapTemp.constEnd(); ++it) { + const QList &itemKeyList = it.value().keys(); + for (auto key : itemKeyList) { + if (key != "pluginloader") { + itemRemoved(it.key(), key); + } + } + for (auto key : itemKeyList) { + if (key != "pluginloader") { + itemAdded(it.key(), key); + } + } + } +} + +bool DockPluginController::eventFilter(QObject *object, QEvent *event) +{ + if (object != qApp || event->type() != QEvent::DynamicPropertyChange) + return false; + + QDynamicPropertyChangeEvent *const dpce = static_cast(event); + const QString propertyName = dpce->propertyName(); + + if (propertyName == PROP_POSITION) + positionChanged(); + else if (propertyName == PROP_DISPLAY_MODE) + displayModeChanged(); + + return false; +} + +bool DockPluginController::pluginCanDock(PluginsItemInterface *plugin) const +{ + // 观察插件是否已经驻留在任务栏上,如果已经驻留在任务栏,则始终显示 + if (plugin->flags() & PluginFlag::Attribute_ForceDock) + return true; + + const QStringList configPlugins = SETTINGCONFIG->value(DOCK_QUICK_PLUGINS).toStringList(); + return configPlugins.contains(plugin->pluginName()); +} + +void DockPluginController::updateDockInfo(PluginsItemInterface * const itemInter, const DockPart &part) +{ + m_proxyInter->updateDockInfo(itemInter, part); + Q_EMIT pluginUpdated(itemInter, part); +} + +void DockPluginController::onConfigChanged(const QString &key, const QVariant &value) +{ + if (key != DOCK_QUICK_PLUGINS) + return; + + QStringList pluginNames = value.toStringList(); + // 这里只处理工具插件(回收站)和系统插件(电源插件) + for (PluginsItemInterface *plugin : plugins()) { + QString itemKey = this->itemKey(plugin); + if (!pluginNames.contains(plugin->pluginName()) && isPluginLoaded(plugin)) { + // 如果当前配置中不包含当前插件,但是当前插件已经加载,那么就移除该插件 + removePluginItem(plugin, itemKey); + QWidget *itemWidget = plugin->itemWidget(itemKey); + if (itemWidget) + itemWidget->setVisible(false); + } else if (pluginNames.contains(plugin->pluginName()) && !isPluginLoaded(plugin)) { + // 如果当前配置中包含当前插件,但是当前插件并未加载,那么就加载该插件 + addPluginItem(plugin, itemKey); + // 只有工具插件是通过QWidget的方式进行显示的,因此,这里只处理工具插件 + if (plugin->flags() & PluginFlag::Type_Tool) { + QWidget *itemWidget = plugin->itemWidget(itemKey); + if (itemWidget) + itemWidget->setVisible(true); + } + } + } +} diff --git a/plugins/pluginmanager/dockplugincontroller.h b/plugins/pluginmanager/dockplugincontroller.h new file mode 100644 index 000000000..1aa09c1cc --- /dev/null +++ b/plugins/pluginmanager/dockplugincontroller.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 DOCKPLUGINCONTROLLER_H +#define DOCKPLUGINCONTROLLER_H + +#include "pluginproxyinterface.h" +#include "pluginloader.h" +#include "dbusutil.h" + +#include +#include +#include + +class PluginsItemInterface; +class PluginAdapter; + +class DockPluginController : public QObject, protected PluginProxyInterface +{ + Q_OBJECT + +public: + explicit DockPluginController(PluginProxyInterface *proxyInter, QObject *parent = Q_NULLPTR); + ~ DockPluginController() override; + + QList plugins() const; // 所有的插件 + QList pluginsInSetting() const; // 控制中心用于可以设置是否显示或隐藏的插件 + QList currentPlugins() const; // 当前使用的插件 + + QString itemKey(PluginsItemInterface *itemInter) const; + QJsonObject metaData(PluginsItemInterface *pluginItem); + + virtual void savePluginValue(PluginsItemInterface *const itemInter, const QString &key, const QVariant &value); + virtual const QVariant getPluginValue(PluginsItemInterface *const itemInter, const QString &key, const QVariant& fallback = QVariant()); + virtual void removePluginValue(PluginsItemInterface * const itemInter, const QStringList &keyList); + void startLoadPlugin(const QStringList &dirs); + +Q_SIGNALS: + void pluginLoadFinished(); + void pluginInserted(PluginsItemInterface *itemInter, QString); + void pluginRemoved(PluginsItemInterface *itemInter); + void pluginUpdated(PluginsItemInterface *, const DockPart); + void requestAppletVisible(PluginsItemInterface *, const QString &, bool); + +protected: + bool isPluginLoaded(PluginsItemInterface *itemInter); + + QObject *pluginItemAt(PluginsItemInterface * const itemInter, const QString &itemKey) const; + PluginsItemInterface *pluginInterAt(const QString &itemKey); + PluginsItemInterface *pluginInterAt(QObject *destItem); + bool eventFilter(QObject *object, QEvent *event) override; + + bool pluginCanDock(PluginsItemInterface *plugin) const; + void updateDockInfo(PluginsItemInterface * const itemInter, const DockPart &part) override; + +private: + // implements PluginProxyInterface + void saveValue(PluginsItemInterface *const itemInter, const QString &key, const QVariant &value) override; + const QVariant getValue(PluginsItemInterface *const itemInter, const QString &key, const QVariant& fallback = QVariant()) override; + void removeValue(PluginsItemInterface * const itemInter, const QStringList &keyList) override; + + void itemAdded(PluginsItemInterface * const itemInter, const QString &itemKey) override; + void itemUpdate(PluginsItemInterface * const itemInter, const QString &itemKey) override; + void itemRemoved(PluginsItemInterface * const itemInter, const QString &itemKey) override; + void requestWindowAutoHide(PluginsItemInterface * const itemInter, const QString &itemKey, const bool autoHide) override; + void requestRefreshWindowVisible(PluginsItemInterface * const itemInter, const QString &itemKey) override; + void requestSetAppletVisible(PluginsItemInterface * const itemInter, const QString &itemKey, const bool visible) override; + PluginsItemInterface *getPluginInterface(PluginsItemInterface * const itemInter); + + void addPluginItem(PluginsItemInterface * const itemInter, const QString &itemKey); + void removePluginItem(PluginsItemInterface * const itemInter, const QString &itemKey); + +private Q_SLOTS: + void startLoader(PluginLoader *loader); + void displayModeChanged(); + void positionChanged(); + void loadPlugin(const QString &pluginFile); + void initPlugin(PluginsItemInterface *interface); + void refreshPluginSettings(); + void onConfigChanged(const QString &key, const QVariant &value); + +private: + QDBusConnectionInterface *m_dbusDaemonInterface; + DockInter *m_dockDaemonInter; + + // interface, "pluginloader", PluginLoader指针对象 + QMap> m_pluginsMap; + + // filepath, interface, loaded + QMap, bool> m_pluginLoadMap; + + QJsonObject m_pluginSettingsObject; + QMap m_pluginAdapterMap; + + PluginProxyInterface *m_proxyInter; +}; + +#endif // ABSTRACTPLUGINSCONTROLLER_H diff --git a/plugins/pluginmanager/iconmanager.cpp b/plugins/pluginmanager/iconmanager.cpp new file mode 100644 index 000000000..00f3df502 --- /dev/null +++ b/plugins/pluginmanager/iconmanager.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2023 ~ 2023 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 "iconmanager.h" +#include "dockplugincontroller.h" +#include "pluginsiteminterface.h" + +#include + +#include +#include + +#define ITEMSPACE 6 +#define ITEMHEIGHT 16 +#define ITEMWIDTH 18 +static QStringList pluginNames = {"power", "sound", "network"}; + +DGUI_USE_NAMESPACE + +IconManager::IconManager(DockPluginController *pluginController, QObject *parent) + : QObject{parent} + , m_pluginController(pluginController) + , m_size(QSize(ITEMWIDTH, ITEMHEIGHT)) + , m_position(Dock::Position::Bottom) +{ +} + +void IconManager::updateSize(QSize size) +{ + m_size = size; +} + +void IconManager::setPosition(Dock::Position position) +{ + m_position = position; +} + +QPixmap IconManager::pixmap() const +{ + QList plugins; + for (const QString &pluginName : pluginNames) { + PluginsItemInterface *plugin = findPlugin(pluginName); + if (plugin) + plugins << plugin; + } + + if (plugins.size() < 2) { + // 缺省图标 + DDciIcon::Theme theme = DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::DarkType ? DDciIcon::Light : DDciIcon::Dark; + DDciIcon dciIcon(QString(":/resources/dock_control.dci")); + return dciIcon.pixmap(QCoreApplication::testAttribute(Qt::AA_UseHighDpiPixmaps) ? 1 : qApp->devicePixelRatio(), ITEMHEIGHT, theme, DDciIcon::Normal); + } + + // 组合图标 + QPixmap pixmap; + if (m_position == Dock::Position::Top || m_position == Dock::Position::Bottom) { + pixmap = QPixmap(ITEMWIDTH * plugins.size() + ITEMSPACE * (plugins.size() + 1), m_size.height() - 8); + } else { + pixmap = QPixmap(m_size.width() - 8, ITEMWIDTH * plugins.size() + ITEMSPACE * (plugins.size() + 1)); + } + + pixmap.fill(Qt::transparent); + QPainter painter(&pixmap); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing); + QColor backColor = DGuiApplicationHelper::ColorType::DarkType == DGuiApplicationHelper::instance()->themeType() ? QColor(20, 20, 20) : Qt::white; + backColor.setAlphaF(0.2); + QPainterPath path; + path.addRoundedRect(pixmap.rect(), 5, 5); + painter.fillPath(path, backColor); + if (m_position == Dock::Position::Top || m_position == Dock::Position::Bottom) { + QPoint pointPixmap(ITEMSPACE, (pixmap.height() - ITEMHEIGHT) / 2); + for (PluginsItemInterface *plugin : plugins) { + QIcon icon = plugin->icon(DockPart::QuickShow); + QPixmap pixmapDraw = icon.pixmap(ITEMHEIGHT, ITEMHEIGHT); + painter.drawPixmap(pointPixmap, pixmapDraw); + pointPixmap.setX(pointPixmap.x() + ITEMWIDTH + ITEMSPACE); + } + } else { + QPoint pointPixmap((pixmap.width() - ITEMWIDTH) / 2, ITEMSPACE); + for (PluginsItemInterface *plugin : plugins) { + QIcon icon = plugin->icon(DockPart::QuickShow); + QPixmap pixmapDraw = icon.pixmap(ITEMHEIGHT, ITEMHEIGHT); + painter.drawPixmap(pointPixmap, pixmapDraw); + pointPixmap.setY(pointPixmap.y() + ITEMWIDTH + ITEMSPACE); + } + } + painter.end(); + return pixmap; +} + +bool IconManager::isFixedPlugin(PluginsItemInterface *plugin) const +{ + return pluginNames.contains(plugin->pluginName()); +} + +PluginsItemInterface *IconManager::findPlugin(const QString &pluginName) const +{ + QList plugins = m_pluginController->currentPlugins(); + for (PluginsItemInterface *plugin : plugins) { + if (plugin->pluginName() == pluginName) + return plugin; + } + + return nullptr; +} diff --git a/plugins/pluginmanager/iconmanager.h b/plugins/pluginmanager/iconmanager.h new file mode 100644 index 000000000..ff5363599 --- /dev/null +++ b/plugins/pluginmanager/iconmanager.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2023 ~ 2023 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 ICONMANAGER_H +#define ICONMANAGER_H + +#include "pluginsiteminterface.h" + +#include +#include + +class DockPluginController; +class PluginsItemInterface; + +class IconManager : public QObject +{ + Q_OBJECT + +public: + explicit IconManager(DockPluginController *pluginController, QObject *parent = nullptr); + void updateSize(QSize size); + void setPosition(Dock::Position position); + QPixmap pixmap() const; + bool isFixedPlugin(PluginsItemInterface *plugin) const; + +private: + PluginsItemInterface *findPlugin(const QString &pluginName) const; + +private: + DockPluginController *m_pluginController; + QSize m_size; + Dock::Position m_position; +}; + +#endif // ICONMANAGER_H diff --git a/plugins/pluginmanager/largerquickitem.cpp b/plugins/pluginmanager/largerquickitem.cpp new file mode 100644 index 000000000..8f805bb4e --- /dev/null +++ b/plugins/pluginmanager/largerquickitem.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 "largerquickitem.h" +#include "pluginsiteminterface.h" + +#include +#include +#include + +#include +#include +#include + +#define BGSIZE 36 +#define ICONWIDTH 24 +#define ICONHEIGHT 24 + +DWIDGET_USE_NAMESPACE + +static QSize expandSize = QSize(20, 20); + +LargerQuickItem::LargerQuickItem(PluginsItemInterface *const pluginInter, const QString &itemKey, QWidget *parent) + : QuickSettingItem(pluginInter, itemKey, parent) + , m_iconWidget(nullptr) + , m_nameLabel(nullptr) + , m_stateLabel(nullptr) + , m_itemWidgetParent(nullptr) +{ + initUi(); +} + +LargerQuickItem::~LargerQuickItem() +{ + QWidget *itemWidget = pluginItem()->itemWidget(QUICK_ITEM_KEY); + if (itemWidget) + itemWidget->setParent(nullptr); +} + +void LargerQuickItem::doUpdate() +{ + if (m_iconWidget && m_nameLabel && m_stateLabel) { + m_iconWidget->update(); + m_nameLabel->setText(QFontMetrics(m_nameLabel->font()).elidedText(pluginItem()->pluginDisplayName(), Qt::TextElideMode::ElideRight, m_nameLabel->width())); + m_stateLabel->setText(QFontMetrics(m_stateLabel->font()).elidedText(pluginItem()->description(), Qt::TextElideMode::ElideRight, m_stateLabel->width())); + } else { + QWidget *itemWidget = pluginItem()->itemWidget(QUICK_ITEM_KEY); + if (itemWidget) { + // 如果插件没有返回图标的显示,则获取插件的itemWidget + itemWidget->update(); + } + } +} + +void LargerQuickItem::detachPlugin() +{ + QWidget *itemWidget = pluginItem()->itemWidget(QUICK_ITEM_KEY); + if (itemWidget && itemWidget->parentWidget() == this) + itemWidget->setParent(m_itemWidgetParent); +} + +QuickSettingItem::QuickItemStyle LargerQuickItem::type() const +{ + return QuickSettingItem::QuickItemStyle::Larger; +} + +bool LargerQuickItem::eventFilter(QObject *obj, QEvent *event) +{ + if (!m_iconWidget) + return QuickSettingItem::eventFilter(obj, event); + + switch (event->type()) { + case QEvent::MouseButtonRelease: { + if (obj->objectName() == "expandLabel") { + // 如果是鼠标的按下事件 + QWidget *widget = pluginItem()->itemPopupApplet(QUICK_ITEM_KEY); + if (widget) + Q_EMIT requestShowChildWidget(widget); + break; + } + if (obj == this) { + QStringList commandArgumend = pluginItem()->itemCommand(itemKey()).split(" "); + if (commandArgumend.size() == 0) + break; + + QString command = commandArgumend.first(); + commandArgumend.removeFirst(); + QProcess::startDetached(command, commandArgumend); + } + break; + } + case QEvent::Resize: { + QLabel *labelWidget = qobject_cast(obj); + if (!labelWidget) + break; + + if (labelWidget == m_nameLabel) { + labelWidget->setText(QFontMetrics(labelWidget->font()).elidedText(pluginItem()->pluginDisplayName(), Qt::TextElideMode::ElideRight, labelWidget->width())); + break; + } + if (labelWidget == m_stateLabel) { + labelWidget->setText(QFontMetrics(labelWidget->font()).elidedText(pluginItem()->description(), Qt::TextElideMode::ElideRight, labelWidget->width())); + } + break; + } + default: + break; + } + + return QuickSettingItem::eventFilter(obj, event); +} + +void LargerQuickItem::showEvent(QShowEvent *event) +{ + QWidget *itemWidget = pluginItem()->itemWidget(QUICK_ITEM_KEY); + if (pluginItem()->icon(DockPart::QuickPanel).isNull() && itemWidget) { + itemWidget->setParent(this); + itemWidget->setVisible(true); + } +} + +void LargerQuickItem::resizeEvent(QResizeEvent *event) +{ + QWidget *itemWidget = pluginItem()->itemWidget(QUICK_ITEM_KEY); + if (pluginItem()->icon(DockPart::QuickPanel).isNull() && itemWidget) { + itemWidget->setFixedSize(size()); + } + + QuickSettingItem::resizeEvent(event); +} + +void LargerQuickItem::initUi() +{ + QWidget *itemWidget = pluginItem()->itemWidget(QUICK_ITEM_KEY); + if (pluginItem()->icon(DockPart::QuickPanel).isNull() && itemWidget) { + m_itemWidgetParent = itemWidget->parentWidget(); + // 如果插件没有返回图标的显示,则获取插件的itemWidget + QHBoxLayout *mainLayout = new QHBoxLayout(this); + itemWidget->setParent(this); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->addWidget(itemWidget); + itemWidget->setVisible(true); + } else { + // 如果插件获取到插件区域的图标,则让其按照图标来组合显示 + // 如果是占用两排的插件,则用横向Layout + QHBoxLayout *mainLayout = new QHBoxLayout(this); + mainLayout->setContentsMargins(10, 0, 10, 0); + mainLayout->setSpacing(0); + mainLayout->addStretch(10); + mainLayout->setAlignment(Qt::AlignCenter); + + // 添加图标 + QWidget *iconWidgetParent = new QWidget(this); + QVBoxLayout *iconLayout = new QVBoxLayout(iconWidgetParent); + iconLayout->setContentsMargins(0, 0, 0, 0); + iconLayout->setSpacing(0); + iconLayout->setAlignment(Qt::AlignCenter); + + m_iconWidget = new QuickIconWidget(pluginItem(), itemKey(), iconWidgetParent); + m_iconWidget->setFixedSize(BGSIZE, BGSIZE); + iconLayout->addWidget(m_iconWidget); + mainLayout->addWidget(iconWidgetParent); + mainLayout->addSpacing(10); + + // 添加中间的名称部分 + QWidget *textWidget = new QWidget(this); + m_nameLabel = new QLabel(textWidget); + m_stateLabel = new QLabel(textWidget); + m_nameLabel->setObjectName("nameLabel"); + m_stateLabel->setObjectName("stateLabel"); + + // 设置图标和文字的属性 + QFont nameFont = DFontSizeManager::instance()->t6(); + nameFont.setBold(true); + QPalette pe; + pe.setColor(QPalette::WindowText, Qt::black); + m_nameLabel->setPalette(pe); + m_stateLabel->setPalette(pe); + m_nameLabel->setFont(nameFont); + m_stateLabel->setFont(DFontSizeManager::instance()->t10()); + m_nameLabel->setText(pluginItem()->pluginDisplayName()); + m_stateLabel->setText(pluginItem()->description()); + m_nameLabel->installEventFilter(this); + m_stateLabel->installEventFilter(this); + + QVBoxLayout *textLayout = new QVBoxLayout(textWidget); + textLayout->setContentsMargins(0, 0, 0, 0); + textLayout->setSpacing(0); + textLayout->addWidget(m_nameLabel); + textLayout->addWidget(m_stateLabel); + textLayout->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + mainLayout->addWidget(textWidget); + + // 添加右侧的展开按钮 + QWidget *expandWidgetParent = new QWidget(this); + QVBoxLayout *expandLayout = new QVBoxLayout(expandWidgetParent); + expandLayout->setSpacing(0); + QLabel *expandLabel = new QLabel(expandWidgetParent); + expandLabel->setObjectName("expandLabel"); + expandLabel->setPixmap(QPixmap(expandFileName())); + expandLabel->setFixedSize(expandSize); + expandLabel->setAutoFillBackground(true); + expandLabel->installEventFilter(this); + expandLayout->addWidget(expandLabel); + pe.setBrush(QPalette::Window, Qt::transparent); + expandLabel->setPalette(pe); + mainLayout->addWidget(expandWidgetParent); + } +} + +QString LargerQuickItem::expandFileName() const +{ + if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType) + return QString(":/icons/resources/arrow-right-dark.svg"); + + return QString(":/icons/resources/arrow-right.svg"); +} + +/** + * @brief QuickIconWidget::QuickIconWidget + * @param pluginInter + * @param parent + * 图标的widget + */ +QuickIconWidget::QuickIconWidget(PluginsItemInterface *pluginInter, const QString &itemKey, QWidget *parent) + : QWidget(parent) + , m_pluginInter(pluginInter) + , m_itemKey(itemKey) +{ +} + +void QuickIconWidget::paintEvent(QPaintEvent *event) +{ + QPixmap pixmapIcon = pluginIcon(); + if (pixmapIcon.isNull()) + return QWidget::paintEvent(event); + + pixmapIcon = pluginIcon(true); + QPainter painter(this); + painter.setRenderHint(QPainter::RenderHint::Antialiasing); + painter.setPen(foregroundColor()); + + DPalette dpa = DPaletteHelper::instance()->palette(this); + QPainter pa(&pixmapIcon); + pa.setCompositionMode(QPainter::CompositionMode_SourceIn); + pa.fillRect(pixmapIcon.rect(), painter.pen().brush()); + // 如果是主图标,则显示阴影背景 + painter.save(); + painter.setPen(Qt::NoPen); + painter.setBrush(dpa.brush(DPalette::ColorRole::Midlight)); + painter.drawEllipse(rect()); + painter.restore(); + QRect rctIcon((rect().width() - pixmapIcon.width()) / 2, (rect().height() - pixmapIcon.height()) / 2, pixmapIcon.width(), pixmapIcon.height()); + painter.drawPixmap(rctIcon, pixmapIcon); +} + +QColor QuickIconWidget::foregroundColor() const +{ + DPalette dpa = DPaletteHelper::instance()->palette(this); + // TODO 此处的颜色是临时获取的,后期需要和设计师确认,改成正规的颜色 + if (m_pluginInter->status() == PluginsItemInterface::PluginMode::Active) + return dpa.color(DPalette::ColorGroup::Active, DPalette::ColorRole::Text); + + if (m_pluginInter->status() == PluginsItemInterface::PluginMode::Deactive) + return dpa.color(DPalette::ColorGroup::Disabled, DPalette::ColorRole::Text); + + return dpa.color(DPalette::ColorGroup::Normal, DPalette::ColorRole::Text); +} + +QPixmap QuickIconWidget::pluginIcon(bool contailGrab) const +{ + QIcon icon = m_pluginInter->icon(DockPart::QuickPanel); + if (icon.isNull() && contailGrab) { + // 如果图标为空,就使用itemWidget的截图作为它的图标,这种一般是适用于老版本插件或者没有实现v23接口的插件 + QWidget *itemWidget = m_pluginInter->itemWidget(m_itemKey); + if (itemWidget) { + itemWidget->setFixedSize(ICONWIDTH, ICONHEIGHT); + return itemWidget->grab(); + } + return QPixmap(); + } + + // 获取icon接口返回的图标 + int pixmapWidth = width(); + int pixmapHeight = height(); + QList iconSizes = icon.availableSizes(); + if (iconSizes.size() > 0) { + QSize size = iconSizes[0]; + if (size.isValid() && !size.isEmpty() && !size.isNull()) { + pixmapWidth = size.width(); + pixmapHeight = size.height(); + } + } + + return icon.pixmap(pixmapWidth, pixmapHeight); +} diff --git a/plugins/pluginmanager/largerquickitem.h b/plugins/pluginmanager/largerquickitem.h new file mode 100644 index 000000000..43abc83f5 --- /dev/null +++ b/plugins/pluginmanager/largerquickitem.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 LARGERQUICKITEM_H +#define LARGERQUICKITEM_H + +#include + +class QuickIconWidget; +class QWidget; +class QLabel; + +// 插件在快捷面板中的展示样式,这个为大图标,展示为两列的那种,例如网络和蓝牙 +class LargerQuickItem : public QuickSettingItem +{ + Q_OBJECT + +public: + LargerQuickItem(PluginsItemInterface *const pluginInter, const QString &itemKey, QWidget *parent = nullptr); + ~LargerQuickItem() override; + void doUpdate() override; + void detachPlugin() override; + + QuickItemStyle type() const override; + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + void showEvent(QShowEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + +private: + void initUi(); + QString expandFileName() const; + +private: + QuickIconWidget *m_iconWidget; + QLabel *m_nameLabel; + QLabel *m_stateLabel; + QWidget *m_itemWidgetParent; +}; + +/** + * @brief The QuickIconWidget class + * 图标的Widget + */ +class QuickIconWidget : public QWidget +{ + Q_OBJECT + +public: + explicit QuickIconWidget(PluginsItemInterface *pluginInter, const QString &itemKey, QWidget *parent = Q_NULLPTR); + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + QColor foregroundColor() const; + QPixmap pluginIcon(bool contailGrab = false) const; + +private: + PluginsItemInterface *m_pluginInter; + QString m_itemKey; +}; + +#endif // MULTIQUICKITEM_H diff --git a/plugins/pluginmanager/linequickitem.cpp b/plugins/pluginmanager/linequickitem.cpp new file mode 100644 index 000000000..1966c6e84 --- /dev/null +++ b/plugins/pluginmanager/linequickitem.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 "linequickitem.h" +#include "pluginsiteminterface.h" + +#include + +#include + +DWIDGET_USE_NAMESPACE + +LineQuickItem::LineQuickItem(PluginsItemInterface *const pluginInter, const QString &itemKey, QWidget *parent) + : QuickSettingItem(pluginInter, itemKey, parent) + , m_centerWidget(pluginInter->itemWidget(QUICK_ITEM_KEY)) + , m_centerParentWidget(nullptr) + , m_effectWidget(new DBlurEffectWidget(this)) +{ + initUi(); + QMetaObject::invokeMethod(this, &LineQuickItem::resizeSelf, Qt::QueuedConnection); +} + +LineQuickItem::~LineQuickItem() +{ + if (m_centerWidget) + m_centerWidget->setParent(nullptr); +} + +void LineQuickItem::doUpdate() +{ + if (m_centerWidget) + m_centerWidget->update(); +} + +void LineQuickItem::detachPlugin() +{ + if (m_centerWidget) + m_centerWidget->setParent(m_centerParentWidget); +} + +QuickSettingItem::QuickItemStyle LineQuickItem::type() const +{ + return QuickSettingItem::QuickItemStyle::Line; +} + +bool LineQuickItem::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == m_centerWidget && event->type() == QEvent::Resize) + resizeSelf(); + + return QuickSettingItem::eventFilter(obj, event); +} + +void LineQuickItem::initUi() +{ + QColor maskColor(Qt::white); + maskColor.setAlphaF(0.8); + m_effectWidget->setMaskColor(maskColor); + m_effectWidget->setBlurRectXRadius(8); + m_effectWidget->setBlurRectYRadius(8); + + // 如果图标不为空 + if (!m_centerWidget) + return; + + m_centerWidget->setVisible(true); + m_centerParentWidget = m_centerWidget->parentWidget(); + + QHBoxLayout *layout = new QHBoxLayout(m_effectWidget); + layout->setContentsMargins(0, 0, 0, 0); + layout->setAlignment(Qt::AlignHCenter); + layout->addWidget(m_centerWidget); + + QHBoxLayout *mainLayout = new QHBoxLayout(this); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->addWidget(m_effectWidget); + + m_centerWidget->installEventFilter(this); +} + +void LineQuickItem::resizeSelf() +{ + if (!m_centerWidget) + return; + + m_effectWidget->setFixedHeight(m_centerWidget->height()); + setFixedHeight(m_centerWidget->height()); +} diff --git a/plugins/pluginmanager/linequickitem.h b/plugins/pluginmanager/linequickitem.h new file mode 100644 index 000000000..e284f37cd --- /dev/null +++ b/plugins/pluginmanager/linequickitem.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 LINEQUICKITEM_H +#define LINEQUICKITEM_H + +#include "quicksettingitem.h" + +namespace Dtk { +namespace Widget { +class DBlurEffectWidget; +} +} + +// 插件在快捷面板中的展示的样式,这个为整行显示的插件,例如声音,亮度调整和音乐播放等 +class LineQuickItem : public QuickSettingItem +{ + Q_OBJECT + +public: + LineQuickItem(PluginsItemInterface *const pluginInter, const QString &itemKey, QWidget *parent = nullptr); + ~LineQuickItem() override; + void doUpdate() override; + void detachPlugin() override; + + QuickItemStyle type() const override; + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private: + void initUi(); + void resizeSelf(); + +private: + QWidget *m_centerWidget; + QWidget *m_centerParentWidget; + Dtk::Widget::DBlurEffectWidget *m_effectWidget; +}; + +#endif // FULLQUICKITEM_H diff --git a/plugins/pluginmanager/pluginadapter/pluginadapter.cpp b/plugins/pluginmanager/pluginadapter/pluginadapter.cpp new file mode 100644 index 000000000..80c39e021 --- /dev/null +++ b/plugins/pluginmanager/pluginadapter/pluginadapter.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 "pluginadapter.h" + +#include + +#define ICONWIDTH 24 +#define ICONHEIGHT 24 + +PluginAdapter::PluginAdapter(PluginsItemInterface_V20 *pluginInter, QPluginLoader *pluginLoader) + : m_pluginInter(pluginInter) + , m_pluginLoader(pluginLoader) +{ +} + +PluginAdapter::~PluginAdapter() +{ + delete m_pluginInter; +} + +const QString PluginAdapter::pluginName() const +{ + return m_pluginInter->pluginName(); +} + +const QString PluginAdapter::pluginDisplayName() const +{ + return m_pluginInter->pluginDisplayName(); +} + +void PluginAdapter::init(PluginProxyInterface *proxyInter) +{ + m_pluginInter->init(proxyInter); +} + +QWidget *PluginAdapter::itemWidget(const QString &itemKey) +{ + return m_pluginInter->itemWidget(itemKey); +} + +QWidget *PluginAdapter::itemTipsWidget(const QString &itemKey) +{ + return m_pluginInter->itemTipsWidget(itemKey); +} + +QWidget *PluginAdapter::itemPopupApplet(const QString &itemKey) +{ + return m_pluginInter->itemPopupApplet(itemKey); +} + +const QString PluginAdapter::itemCommand(const QString &itemKey) +{ + return m_pluginInter->itemCommand(itemKey); +} + +const QString PluginAdapter::itemContextMenu(const QString &itemKey) +{ + return m_pluginInter->itemContextMenu(itemKey); +} + +void PluginAdapter::invokedMenuItem(const QString &itemKey, const QString &menuId, const bool checked) +{ + m_pluginInter->invokedMenuItem(itemKey, menuId, checked); +} + +int PluginAdapter::itemSortKey(const QString &itemKey) +{ + return m_pluginInter->itemSortKey(itemKey); +} + +void PluginAdapter::setSortKey(const QString &itemKey, const int order) +{ + m_pluginInter->setSortKey(itemKey, order); +} + +bool PluginAdapter::itemAllowContainer(const QString &itemKey) +{ + return m_pluginInter->itemAllowContainer(itemKey); +} + +bool PluginAdapter::itemIsInContainer(const QString &itemKey) +{ + return m_pluginInter->itemIsInContainer(itemKey); +} + +void PluginAdapter::setItemIsInContainer(const QString &itemKey, const bool container) +{ + m_pluginInter->setItemIsInContainer(itemKey, container); +} + +bool PluginAdapter::pluginIsAllowDisable() +{ + return m_pluginInter->pluginIsAllowDisable(); +} + +bool PluginAdapter::pluginIsDisable() +{ + return m_pluginInter->pluginIsDisable(); +} + +void PluginAdapter::pluginStateSwitched() +{ + m_pluginInter->pluginStateSwitched(); +} + +void PluginAdapter::displayModeChanged(const Dock::DisplayMode displayMode) +{ + m_pluginInter->displayModeChanged(displayMode); +} + +void PluginAdapter::positionChanged(const Dock::Position position) +{ + m_pluginInter->positionChanged(position); +} + +void PluginAdapter::refreshIcon(const QString &itemKey) +{ + m_pluginInter->refreshIcon(itemKey); +} + +void PluginAdapter::pluginSettingsChanged() +{ + m_pluginInter->pluginSettingsChanged(); +} + +PluginsItemInterface::PluginType PluginAdapter::type() +{ + switch (m_pluginInter->type()) { + case PluginsItemInterface_V20::PluginType::Fixed: + return PluginsItemInterface::PluginType::Fixed; + case PluginsItemInterface_V20::PluginType::Normal: + return PluginsItemInterface::PluginType::Normal; + } + return PluginsItemInterface::PluginType::Normal; +} + +PluginsItemInterface::PluginSizePolicy PluginAdapter::pluginSizePolicy() const +{ + switch (m_pluginInter->pluginSizePolicy()) { + case PluginsItemInterface_V20::PluginSizePolicy::Custom: + return PluginsItemInterface::PluginSizePolicy::Custom; + case PluginsItemInterface_V20::PluginSizePolicy::System: + return PluginsItemInterface::PluginSizePolicy::System; + } + return PluginsItemInterface::PluginSizePolicy::Custom; +} + +QIcon PluginAdapter::icon(const DockPart &dockPart, DGuiApplicationHelper::ColorType themeType) +{ + QWidget *itemWidget = m_pluginInter->itemWidget(m_itemKey); + if (!itemWidget) + return QIcon(); + + switch (dockPart) { + case DockPart::QuickPanel: + case DockPart::SystemPanel: { + // 如果图标为空,就使用itemWidget的截图作为它的图标,这种一般是适用于老版本插件或者没有实现v23接口的插件 + QSize oldSize = itemWidget->size(); + itemWidget->setFixedSize(ICONWIDTH, ICONHEIGHT); + QPixmap pixmap = itemWidget->grab(); + itemWidget->setFixedSize(oldSize); + return pixmap; + } + default: break; + } + + return QIcon(); +} + +PluginsItemInterface::PluginMode PluginAdapter::status() const +{ + return PluginMode::Active; +} + +QString PluginAdapter::description() const +{ + return m_pluginInter->pluginDisplayName(); +} + +PluginFlags PluginAdapter::flags() const +{ + if (m_pluginLoader->fileName().contains(TRAY_PATH)) + return PluginFlag::Type_Tray | PluginFlag::Attribute_CanDrag | PluginFlag::Attribute_CanInsert; + + return PluginsItemInterface::flags(); +} + +void PluginAdapter::setItemKey(const QString &itemKey) +{ + m_itemKey = itemKey; +} diff --git a/plugins/pluginmanager/pluginadapter/pluginadapter.h b/plugins/pluginmanager/pluginadapter/pluginadapter.h new file mode 100644 index 000000000..49ecd3135 --- /dev/null +++ b/plugins/pluginmanager/pluginadapter/pluginadapter.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 PLUGINADAPTER_H +#define PLUGINADAPTER_H + +#include "pluginsiteminterface.h" +#include "pluginsiteminterface_v20.h" + +#include + +/** 适配器,当加载到v20插件的时候,通过该接口来转成v23接口的插件 + * @brief The PluginAdapter class + */ + +class PluginAdapter : public QObject, public PluginsItemInterface +{ + Q_OBJECT + Q_INTERFACES(PluginsItemInterface) + +public: + PluginAdapter(PluginsItemInterface_V20 *pluginInter, QPluginLoader *pluginLoader); + ~PluginAdapter(); + + const QString pluginName() const override; + const QString pluginDisplayName() const override; + void init(PluginProxyInterface *proxyInter) override; + QWidget *itemWidget(const QString &itemKey) override; + + QWidget *itemTipsWidget(const QString &itemKey) override; + QWidget *itemPopupApplet(const QString &itemKey) override; + const QString itemCommand(const QString &itemKey) override; + const QString itemContextMenu(const QString &itemKey) override; + void invokedMenuItem(const QString &itemKey, const QString &menuId, const bool checked) override; + int itemSortKey(const QString &itemKey) override; + void setSortKey(const QString &itemKey, const int order) override; + bool itemAllowContainer(const QString &itemKey) override; + bool itemIsInContainer(const QString &itemKey) override; + void setItemIsInContainer(const QString &itemKey, const bool container) override; + + bool pluginIsAllowDisable() override; + bool pluginIsDisable() override; + void pluginStateSwitched() override; + void displayModeChanged(const Dock::DisplayMode displayMode) override; + void positionChanged(const Dock::Position position) override; + void refreshIcon(const QString &itemKey) override; + void pluginSettingsChanged() override; + PluginType type() override; + PluginSizePolicy pluginSizePolicy() const override; + + QIcon icon(const DockPart &dockPart, DGuiApplicationHelper::ColorType themeType = DGuiApplicationHelper::instance()->themeType()) override; + PluginMode status() const override; + QString description() const override; + PluginFlags flags() const override; + + void setItemKey(const QString &itemKey); + +private: + PluginsItemInterface_V20 *m_pluginInter; + QString m_itemKey; + QPluginLoader *m_pluginLoader; +}; + +#endif // PLUGINADAPTER_H diff --git a/plugins/pluginmanager/pluginadapter/pluginsiteminterface_v20.h b/plugins/pluginmanager/pluginadapter/pluginsiteminterface_v20.h new file mode 100644 index 000000000..913aa0e9d --- /dev/null +++ b/plugins/pluginmanager/pluginadapter/pluginsiteminterface_v20.h @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 PLUGINSITEMINTERFACE_V20_H +#define PLUGINSITEMINTERFACE_V20_H + +#include "pluginproxyinterface.h" + +#include +#include + +/// +/// \brief The PluginsItemInterface_V20 class +/// the dock plugins item interface, all dock plugins should +/// inheirt this class and override all pure virtual function. +/// +class PluginsItemInterface_V20 +{ +public: + enum PluginType { + Normal, + Fixed + }; + + /** + * @brief Plugin size policy + */ + enum PluginSizePolicy { + System = 1 << 0, // Follow the system + Custom = 1 << 1 // The custom + }; + + /// + /// \brief ~PluginsItemInterface_V20 + /// DON'T try to delete m_proxyInter. + /// + virtual ~PluginsItemInterface_V20() {} + + /// + /// \brief pluginName + /// tell dock the unique plugin id + /// \return + /// + virtual const QString pluginName() const = 0; + virtual const QString pluginDisplayName() const { return QString(); } + + /// + /// \brief init + /// init your plugins, you need to save proxyInter to m_proxyInter + /// member variable. but you shouldn't free this pointer. + /// \param proxyInter + /// DON'T try to delete this pointer. + /// + virtual void init(PluginProxyInterface *proxyInter) = 0; + /// + /// \brief itemWidget + /// your plugin item widget, each item should have a unique key. + /// \param itemKey + /// your widget' unqiue key. + /// \return + /// + virtual QWidget *itemWidget(const QString &itemKey) = 0; + + /// + /// \brief itemTipsWidget + /// override this function if your item want to have a tips. + /// the tips will shown when user hover your item. + /// nullptr will be ignored. + /// \param itemKey + /// \return + /// + virtual QWidget *itemTipsWidget(const QString &itemKey) {Q_UNUSED(itemKey); return nullptr;} + /// + /// \brief itemPopupApplet + /// override this function if your item wants to have an popup applet. + /// the popup applet will shown when user click your item. + /// + /// Tips: + /// dock should receive mouse press/release event to check user mouse operate, + /// if your item filter mouse event, this function will not be called. + /// so if you override mouse event and want to use popup applet, you + /// should pass event to your parent use QWidget::someEvent(e); + /// \param itemKey + /// \return + /// + virtual QWidget *itemPopupApplet(const QString &itemKey) {Q_UNUSED(itemKey); return nullptr;} + /// + /// \brief itemCommand + /// execute spec command when user clicked your item. + /// ensure your command do not get user input. + /// + /// empty string will be ignored. + /// \param itemKey + /// \return + /// + virtual const QString itemCommand(const QString &itemKey) {Q_UNUSED(itemKey); return QString();} + + /// + /// \brief itemContextMenu + /// context menu is shown when RequestPopupMenu called. + /// \param itemKey + /// \return + /// + virtual const QString itemContextMenu(const QString &itemKey) {Q_UNUSED(itemKey); return QString();} + /// + /// \brief invokedMenuItem + /// call if context menu item is clicked + /// \param itemKey + /// \param itemId + /// menu item id + /// \param checked + /// + virtual void invokedMenuItem(const QString &itemKey, const QString &menuId, const bool checked) {Q_UNUSED(itemKey); Q_UNUSED(menuId); Q_UNUSED(checked);} + + /// + /// \brief itemSortKey + /// tell dock where your item wants to put on. + /// + /// this index is start from 1 and + /// 0 for left side + /// -1 for right side + /// \param itemKey + /// \return + /// + virtual int itemSortKey(const QString &itemKey) {Q_UNUSED(itemKey); return 1;} + /// + /// \brief setSortKey + /// save your item new position + /// sort key will be changed when plugins order + /// changed(by user drag-drop) + /// \param itemKey + /// \param order + /// + virtual void setSortKey(const QString &itemKey, const int order) {Q_UNUSED(itemKey); Q_UNUSED(order);} + + /// + /// \brief itemAllowContainer + /// tell dock is your item allow to move into container + /// + /// if your item placed into container, popup tips and popup + /// applet will be disabled. + /// \param itemKey + /// \return + /// + virtual bool itemAllowContainer(const QString &itemKey) {Q_UNUSED(itemKey); return false;} + /// + /// \brief itemIsInContainer + /// tell dock your item is in container, this function + /// called at item init and if your item enable container. + /// \param itemKey + /// \return + /// + virtual bool itemIsInContainer(const QString &itemKey) {Q_UNUSED(itemKey); return false;} + /// + /// \brief setItemIsInContainer + /// save your item new state. + /// this function called when user drag out your item from + /// container or user drop item into container(if your item + /// allow drop into container). + /// \param itemKey + /// \param container + /// + virtual void setItemIsInContainer(const QString &itemKey, const bool container) {Q_UNUSED(itemKey); Q_UNUSED(container);} + + virtual bool pluginIsAllowDisable() { return false; } + virtual bool pluginIsDisable() { return false; } + virtual void pluginStateSwitched() {} + + /// + /// \brief displayModeChanged + /// override this function to receive display mode changed signal + /// \param displayMode + /// + virtual void displayModeChanged(const Dock::DisplayMode displayMode) {Q_UNUSED(displayMode);} + /// + /// \brief positionChanged + /// override this function to receive dock position changed signal + /// \param position + /// + virtual void positionChanged(const Dock::Position position) {Q_UNUSED(position);} + + /// + /// \brief refreshIcon + /// refresh item icon, its triggered when system icon theme changed. + /// \param itemKey + /// item key + /// + virtual void refreshIcon(const QString &itemKey) { Q_UNUSED(itemKey); } + + /// + /// \brief displayMode + /// get current dock display mode + /// \return + /// + inline Dock::DisplayMode displayMode() const + { + return qApp->property(PROP_DISPLAY_MODE).value(); + } + + /// + /// \brief position + /// get current dock position + /// \return + /// + inline Dock::Position position() const + { + return qApp->property(PROP_POSITION).value(); + } + + /// + /// \brief settingsChanged + /// override this function to receive plugin settings changed signal(DeepinSync) + /// + virtual void pluginSettingsChanged() {} + + /// + /// \brief type + /// default plugin add dock right,fixed plugin add to dock fixed area + /// + virtual PluginType type() { return Normal; } + + /// + /// \brief plugin size policy + /// default plugin size policy + /// + virtual PluginSizePolicy pluginSizePolicy() const { return System; } + +protected: + /// + /// \brief m_proxyInter + /// NEVER delete this object. + /// + PluginProxyInterface *m_proxyInter = nullptr; +}; + +QT_BEGIN_NAMESPACE + +#define ModuleInterfaceV20_iid "com.deepin.dock.PluginsItemInterface" + +Q_DECLARE_INTERFACE(PluginsItemInterface_V20, ModuleInterfaceV20_iid) +QT_END_NAMESPACE + +#endif // PLUGINSITEMINTERFACE_H diff --git a/plugins/pluginmanager/pluginchildpage.cpp b/plugins/pluginmanager/pluginchildpage.cpp new file mode 100644 index 000000000..ee8fbe0ff --- /dev/null +++ b/plugins/pluginmanager/pluginchildpage.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 "pluginchildpage.h" + +#include +#include + +#include +#include +#include +#include +#include + +DWIDGET_USE_NAMESPACE +DGUI_USE_NAMESPACE + +PluginChildPage::PluginChildPage(QWidget *parent) + : QWidget(parent) + , m_headerWidget(new QWidget(this)) + , m_back(new QPushButton(m_headerWidget)) + , m_title(new QLabel(m_headerWidget)) + , m_container(new QWidget(this)) + , m_topWidget(nullptr) + , m_containerLayout(new QVBoxLayout(m_container)) +{ + initUi(); + initConnection(); +} + +PluginChildPage::~PluginChildPage() +{ +} + +void PluginChildPage::pushWidget(QWidget *widget) +{ + // 首先将界面其他的窗体移除 + for (int i = m_containerLayout->count() - 1; i >= 0; i--) { + QLayoutItem *item = m_containerLayout->itemAt(i); + item->widget()->removeEventFilter(this); + item->widget()->hide(); + m_containerLayout->removeItem(item); + } + m_topWidget = widget; + if (widget) { + widget->installEventFilter(this); + m_containerLayout->addWidget(widget); + widget->show(); + } + QMetaObject::invokeMethod(this, &PluginChildPage::resetHeight, Qt::QueuedConnection); +} + +void PluginChildPage::setTitle(const QString &text) +{ + m_title->setText(text); +} + +void PluginChildPage::initUi() +{ + m_back->setIcon(backPixmap()); + m_back->setFixedWidth(24); + m_back->setFlat(true); + m_title->setAlignment(Qt::AlignCenter); + QHBoxLayout *headerLayout = new QHBoxLayout(m_headerWidget); + headerLayout->setContentsMargins(11, 12, 24 + 11, 12); + headerLayout->setSpacing(0); + headerLayout->addWidget(m_back); + headerLayout->addWidget(m_title); + m_headerWidget->setFixedHeight(48); + + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(0); + + mainLayout->addWidget(m_headerWidget); + mainLayout->addWidget(m_container); + m_containerLayout->setContentsMargins(11, 0, 11, 0); + m_containerLayout->setSpacing(0); +} + +void PluginChildPage::initConnection() +{ + connect(m_back, &QPushButton::clicked, this, &PluginChildPage::back); +} + +bool PluginChildPage::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == m_topWidget && event->type() == QEvent::Resize) { + resetHeight(); + } + return QWidget::eventFilter(watched, event); +} + +void PluginChildPage::resetHeight() +{ + QMargins m = m_containerLayout->contentsMargins(); + m_container->setFixedHeight(m.top() + m.bottom() + (m_topWidget ? m_topWidget->height() : 0)); + setFixedHeight(m_headerWidget->height() + m_container->height()); +} + +QPixmap PluginChildPage::backPixmap() const +{ + QPixmap pixmap(16, 16); + pixmap.fill(Qt::transparent); + + // 设置背景色 + QColor backColor; + if (DGuiApplicationHelper::ColorType::DarkType == DGuiApplicationHelper::instance()->themeType()) { + backColor = Qt::black; + backColor.setAlphaF(0.05); + } else { + backColor = Qt::white; + backColor.setAlphaF(0.2); + } + QPainter painter(&pixmap); + painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing); + painter.setPen(Qt::NoPen); + QPainterPath path; + path.addEllipse(pixmap.rect()); + painter.fillPath(path, backColor); + QSize arrowSize(10, 10); + QPixmap arrowPixmap = DStyle::standardIcon(style(), DStyle::SP_ArrowLeave).pixmap(arrowSize); + // 让其居中显示 + QSize backSize = pixmap.size(); + painter.drawPixmap((backSize.width() - arrowSize.width()) / 2, (backSize.height() - arrowSize.height()) / 2, + arrowSize.width(), arrowSize.height(), arrowPixmap); + painter.end(); + return pixmap; +} diff --git a/plugins/pluginmanager/pluginchildpage.h b/plugins/pluginmanager/pluginchildpage.h new file mode 100644 index 000000000..a81c52789 --- /dev/null +++ b/plugins/pluginmanager/pluginchildpage.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 PLUGINCHILDPAGE_H +#define PLUGINCHILDPAGE_H + +#include + +class QPushButton; +class QLabel; +class QVBoxLayout; + +class PluginChildPage : public QWidget +{ + Q_OBJECT + +public: + explicit PluginChildPage(QWidget *parent); + ~PluginChildPage() override; + void pushWidget(QWidget *widget); + void setTitle(const QString &text); + +Q_SIGNALS: + void back(); + +protected: + bool eventFilter(QObject *watched, QEvent *event) override; + +private: + void initUi(); + void initConnection(); + void resetHeight(); + QPixmap backPixmap() const; + +private: + QWidget *m_headerWidget; + QPushButton *m_back; + QLabel *m_title; + QWidget *m_container; + QWidget *m_topWidget; + QVBoxLayout *m_containerLayout; +}; + +#endif // PLUGINCHILDPAGE_H diff --git a/plugins/pluginmanager/pluginmanager.cpp b/plugins/pluginmanager/pluginmanager.cpp new file mode 100644 index 000000000..4704e29c4 --- /dev/null +++ b/plugins/pluginmanager/pluginmanager.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2023 ~ 2023 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 "pluginmanager.h" +#include "dockplugincontroller.h" +#include "quicksettingcontainer.h" +#include "iconmanager.h" + +#include + +PluginManager::PluginManager(QObject *parent) + : m_dockController(nullptr) + , m_iconManager(nullptr) +{ +} + +const QString PluginManager::pluginName() const +{ + return "pluginManager"; +} + +const QString PluginManager::pluginDisplayName() const +{ + return "pluginManager"; +} + +void PluginManager::init(PluginProxyInterface *proxyInter) +{ + if (m_proxyInter == proxyInter) + return; + + m_proxyInter = proxyInter; + + m_dockController.reset(new DockPluginController(proxyInter)); + m_quickContainer.reset(new QuickSettingContainer(m_dockController.data())); + m_iconManager.reset(new IconManager(m_dockController.data())); + m_iconManager->setPosition(position()); + + connect(m_dockController.data(), &DockPluginController::pluginInserted, this, [ this ](PluginsItemInterface *itemInter) { + if (m_iconManager->isFixedPlugin(itemInter)) { + m_proxyInter->itemUpdate(this, pluginName()); + } + }); + connect(m_dockController.data(), &DockPluginController::pluginUpdated, this, [ this ](PluginsItemInterface *itemInter) { + if (m_iconManager->isFixedPlugin(itemInter)) { + m_proxyInter->itemUpdate(this, pluginName()); + } + }); + connect(m_dockController.data(), &DockPluginController::pluginRemoved, this, [ this ](PluginsItemInterface *itemInter) { + if (m_iconManager->isFixedPlugin(itemInter)) { + m_proxyInter->itemUpdate(this, pluginName()); + } + }); + connect(m_dockController.data(), &DockPluginController::requestAppletVisible, this, [ this ](PluginsItemInterface *itemInter, const QString &itemKey, bool visible) { + if (visible) { + QWidget *appletWidget = itemInter->itemPopupApplet(itemKey); + if (appletWidget) + m_quickContainer->showPage(appletWidget, itemInter); + } else { + // 显示主面板 + m_quickContainer->topLevelWidget()->hide(); + } + }); + connect(m_dockController.data(), &DockPluginController::pluginLoadFinished, this, &PluginManager::pluginLoadFinished); + + // 开始加载插件 + m_dockController->startLoadPlugin(getPluginPaths()); + + m_proxyInter->itemAdded(this, pluginName()); +} + +QWidget *PluginManager::itemWidget(const QString &itemKey) +{ + Q_UNUSED(itemKey); + return nullptr; +} + +QWidget *PluginManager::itemPopupApplet(const QString &itemKey) +{ + if (itemKey == QUICK_ITEM_KEY) { + // 返回快捷面板 + return m_quickContainer.data(); + } + + return nullptr; +} + +QIcon PluginManager::icon(const DockPart &dockPart, DGuiApplicationHelper::ColorType themeType) +{ + if (dockPart == DockPart::QuickShow) { + return m_iconManager->pixmap(); + } + + return QIcon(); +} + +PluginFlags PluginManager::flags() const +{ + // 当前快捷插件组合区域只支持在快捷区域显示 + return PluginFlag::Type_Common | PluginFlag::Attribute_ForceDock; +} + +PluginsItemInterface::PluginSizePolicy PluginManager::pluginSizePolicy() const +{ + return PluginSizePolicy::Custom; +} + +bool PluginManager::eventHandler(QEvent *event) +{ + if (event->type() == QEvent::Resize) { + QResizeEvent *resizeEvent = static_cast(event); + m_iconManager->updateSize(resizeEvent->size()); + } + return PluginsItemInterface::eventHandler(event); +} + +void PluginManager::positionChanged(const Dock::Position position) +{ + m_iconManager->setPosition(position); + m_proxyInter->itemUpdate(this, pluginName()); +} + +QList PluginManager::plugins() const +{ + return m_dockController->plugins(); +} + +QList PluginManager::pluginsInSetting() const +{ + return m_dockController->pluginsInSetting(); +} + +QList PluginManager::currentPlugins() const +{ + return m_dockController->currentPlugins(); +} + +QString PluginManager::itemKey(PluginsItemInterface *itemInter) const +{ + return m_dockController->itemKey(itemInter); +} + +QJsonObject PluginManager::metaData(PluginsItemInterface *itemInter) const +{ + return m_dockController->metaData(itemInter); +} + +#ifndef QT_DEBUG +static QStringList getPathFromConf(const QString &key) { + QSettings set("/etc/deepin/dde-dock.conf", QSettings::IniFormat); + auto value = set.value(key).toString(); + if (!value.isEmpty()) { + return value.split(':'); + } + + return QStringList(); +} +#endif + +QStringList PluginManager::getPluginPaths() const +{ + QStringList pluginPaths; +#ifdef QT_DEBUG + pluginPaths << QString("%1/..%2").arg(qApp->applicationDirPath()).arg(QUICK_PATH) + << QString("%1/..%2").arg(qApp->applicationDirPath()).arg(PLUGIN_PATH) + << QString("%1/..%2").arg(qApp->applicationDirPath()).arg(TRAY_PATH); +#else + pluginPaths << QString("/usr/lib/dde-dock%1").arg(QUICK_PATH) + << QString("/usr/lib/dde-dock%1").arg(PLUGIN_PATH) + << QString("/usr/lib/dde-dock%1").arg(TRAY_PATH); + + const QStringList pluginsDirs = (getPathFromConf("PATH") << getPathFromConf("SYSTEM_TRAY_PATH")); + if (!pluginsDirs.isEmpty()) + pluginPaths << pluginsDirs; +#endif + + return pluginPaths; +} diff --git a/plugins/pluginmanager/pluginmanager.h b/plugins/pluginmanager/pluginmanager.h new file mode 100644 index 000000000..e054e5ebd --- /dev/null +++ b/plugins/pluginmanager/pluginmanager.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 ~ 2023 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 PLUGINMANAGER_H +#define PLUGINMANAGER_H + +#include "pluginsiteminterface.h" +#include "pluginmanagerinterface.h" + +#include + +class DockPluginController; +class QuickSettingContainer; +class IconManager; + +class PluginManager : public PluginManagerInterface, public PluginsItemInterface +{ + Q_OBJECT + Q_INTERFACES(PluginsItemInterface) + Q_PLUGIN_METADATA(IID "com.deepin.dock.PluginsItemInterface" FILE "pluginmanager.json") + +public: + explicit PluginManager(QObject *parent = nullptr); + + const QString pluginName() const override; + const QString pluginDisplayName() const override; + void init(PluginProxyInterface *proxyInter) override; + QWidget *itemWidget(const QString &itemKey) override; + QWidget *itemPopupApplet(const QString &itemKey) override; + QIcon icon(const DockPart &dockPart, DGuiApplicationHelper::ColorType themeType) override; + PluginFlags flags() const override; + PluginSizePolicy pluginSizePolicy() const override; + +protected: + bool eventHandler(QEvent *event) override; + void positionChanged(const Dock::Position position) override; + +protected: + // 实现PluginManagerInterface接口,用于向dock提供所有已经加载的插件 + QList plugins() const override; + QList pluginsInSetting() const override; + QList currentPlugins() const override; + QString itemKey(PluginsItemInterface *itemInter) const override; + QJsonObject metaData(PluginsItemInterface *itemInter) const override; + +private: + QStringList getPluginPaths() const; + +private: + QSharedPointer m_dockController; + QSharedPointer m_quickContainer; + QSharedPointer m_iconManager; +}; + +#endif // PLUGINMANAGER_H diff --git a/plugins/pluginmanager/pluginmanager.json b/plugins/pluginmanager/pluginmanager.json new file mode 100644 index 000000000..b8397f765 --- /dev/null +++ b/plugins/pluginmanager/pluginmanager.json @@ -0,0 +1,4 @@ +{ + "api": "2.0.0", + "order":0 +} diff --git a/plugins/pluginmanager/pluginmanager.qrc b/plugins/pluginmanager/pluginmanager.qrc new file mode 100644 index 000000000..41792f803 --- /dev/null +++ b/plugins/pluginmanager/pluginmanager.qrc @@ -0,0 +1,5 @@ + + + resources/dock_control.dci + + diff --git a/plugins/pluginmanager/quicksettingcontainer.cpp b/plugins/pluginmanager/quicksettingcontainer.cpp new file mode 100644 index 000000000..fb56a2206 --- /dev/null +++ b/plugins/pluginmanager/quicksettingcontainer.cpp @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 "quicksettingcontainer.h" +#include "dockplugincontroller.h" +#include "pluginsiteminterface.h" +#include "quicksettingitem.h" +#include "pluginchildpage.h" +#include "utils.h" +#include "quickdragcore.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +DWIDGET_USE_NAMESPACE + +struct QuickDragInfo { + QPoint dragPosition; + QWidget *dragItem = nullptr; + PluginsItemInterface *pluginInter = nullptr; + void reset() { + dragPosition.setX(0); + dragPosition.setY(0); + dragItem = nullptr; + pluginInter = nullptr; + } + bool isNull() { + return !dragItem; + } +} QuickDragInfo; + +#define ITEMWIDTH 70 +#define ITEMHEIGHT 60 +#define ITEMSPACE 10 +#define COLUMNCOUNT 4 + +QuickSettingContainer::QuickSettingContainer(DockPluginController *pluginController, QWidget *parent) + : QWidget(parent) + , m_switchLayout(new QStackedLayout(this)) + , m_mainWidget(new QWidget(this)) + , m_pluginWidget(new QWidget(m_mainWidget)) + , m_pluginLayout(new QGridLayout(m_pluginWidget)) + , m_componentWidget(new QWidget(m_mainWidget)) + , m_mainlayout(new QVBoxLayout(m_mainWidget)) + , m_pluginController(pluginController) + , m_childPage(new PluginChildPage(this)) + , m_dragInfo(new struct QuickDragInfo) + , m_childShowPlugin(nullptr) +{ + initUi(); + initConnection(); + m_childPage->installEventFilter(this); + setMouseTracking(true); +} + +QuickSettingContainer::~QuickSettingContainer() +{ + delete m_dragInfo; +} + +void QuickSettingContainer::showPage(QWidget *widget, PluginsItemInterface *pluginInter) +{ + if (widget && pluginInter && widget != m_mainWidget) { + m_childShowPlugin = pluginInter; + m_childPage->setTitle(pluginInter->pluginDisplayName()); + m_childPage->pushWidget(widget); + m_switchLayout->setCurrentWidget(m_childPage); + } else { + m_childShowPlugin = nullptr; + m_switchLayout->setCurrentIndex(0); + } + + onResizeView(); +} + +bool QuickSettingContainer::eventFilter(QObject *watched, QEvent *event) +{ + switch (event->type()) { + case QEvent::Resize: { + onResizeView(); + break; + } + case QEvent::MouseButtonPress: { + QMouseEvent *mouseEvent = static_cast(event); + QuickSettingItem *item = qobject_cast(watched); + if (item) { + m_dragInfo->dragPosition = mouseEvent->pos(); + m_dragInfo->dragItem = item; + m_dragInfo->pluginInter = item->pluginItem(); + } + break; + } + case QEvent::MouseButtonRelease: { + m_dragInfo->reset(); + break; + } + default: + break; + } + + return QWidget::eventFilter(watched, event); +} + +void QuickSettingContainer::showEvent(QShowEvent *event) +{ + // 当面板显示的时候,直接默认显示快捷面板的窗口 + QWidget::showEvent(event); + if (m_switchLayout->currentWidget() != m_mainWidget) { + m_childPage->pushWidget(nullptr); + m_switchLayout->setCurrentWidget(m_mainWidget); + onResizeView(); + } +} + +void QuickSettingContainer::appendPlugin(PluginsItemInterface *itemInter, QString itemKey, bool needLayout) +{ + QuickSettingItem *quickItem = QuickSettingFactory::createQuickWidget(itemInter, itemKey); + if (!quickItem) + return; + + quickItem->setParent(m_pluginWidget); + quickItem->setMouseTracking(true); + quickItem->installEventFilter(this); + connect(quickItem, &QuickSettingItem::requestShowChildWidget, this, &QuickSettingContainer::onShowChildWidget); + m_quickSettings << quickItem; + if (quickItem->type() == QuickSettingItem::QuickItemStyle::Line) { + // 插件位置占据整行,例如声音、亮度和音乐等 + m_componentWidget->layout()->addWidget(quickItem); + updateFullItemLayout(); + } else if (needLayout) { + // 插件占据两行或者一行 + updateItemLayout(); + } + + onResizeView(); +} + +void QuickSettingContainer::onPluginRemove(PluginsItemInterface *itemInter) +{ + QList::Iterator removeItemIter = std::find_if(m_quickSettings.begin(), m_quickSettings.end(), [ = ](QuickSettingItem *item)->bool { + return item->pluginItem() == itemInter; + }); + + if (removeItemIter == m_quickSettings.end()) + return; + + QuickSettingItem *removeItem = *removeItemIter; + removeItem->detachPlugin(); + + if (removeItem->type() == QuickSettingItem::QuickItemStyle::Line) + m_componentWidget->layout()->removeWidget(removeItem); + else + m_pluginLayout->removeWidget(removeItem); + + m_quickSettings.removeOne(removeItem); + removeItem->deleteLater(); + if (m_childShowPlugin == itemInter) + showPage(nullptr); + + updateItemLayout(); + updateFullItemLayout(); + onResizeView(); +} + +void QuickSettingContainer::onShowChildWidget(QWidget *childWidget) +{ + QuickSettingItem *quickWidget = qobject_cast(sender()); + if (!quickWidget) + return; + + showPage(childWidget, quickWidget->pluginItem()); +} + +void QuickSettingContainer::mouseMoveEvent(QMouseEvent *event) +{ + if (m_dragInfo->isNull()) + return; + + QPoint pointCurrent = event->pos(); + if (qAbs(m_dragInfo->dragPosition.x() - pointCurrent.x()) > 5 + || qAbs(m_dragInfo->dragPosition.y() - pointCurrent.y()) > 5) { + QuickSettingItem *moveItem = qobject_cast(m_dragInfo->dragItem); + QuickIconDrag *drag = new QuickIconDrag(this, moveItem->dragPixmap()); + QuickPluginMimeData *mimedata = new QuickPluginMimeData(m_dragInfo->pluginInter, drag); + drag->setMimeData(mimedata); + drag->setDragHotPot(m_dragInfo->dragPosition); + + m_dragInfo->reset(); + drag->exec(Qt::CopyAction); + } +} + +void QuickSettingContainer::updateItemLayout() +{ + // 清空之前的控件,重新添加 + while (m_pluginLayout->count() > 0) + m_pluginLayout->takeAt(0); + + // 将插件按照两列和一列的顺序来进行排序 + QMap> quickSettings; + QMap> orderQuickSettings; + for (QuickSettingItem *item : m_quickSettings) { + QuickSettingItem::QuickItemStyle type = item->type(); + if (type == QuickSettingItem::QuickItemStyle::Line) + continue; + + QJsonObject metaData = m_pluginController->metaData(item->pluginItem()); + if (metaData.contains("order")) + orderQuickSettings[type][item] = metaData.value("order").toInt(); + else + quickSettings[type] << item; + } + // 将需要排序的插件按照顺序插入到原来的数组中 + for (auto itQuick = orderQuickSettings.begin(); itQuick != orderQuickSettings.end(); itQuick++) { + QuickSettingItem::QuickItemStyle type = itQuick.key(); + QMap &orderQuicks = itQuick.value(); + for (auto it = orderQuicks.begin(); it != orderQuicks.end(); it++) { + int index = it.value(); + if (index >= 0 && index < quickSettings[type].size()) + quickSettings[type][index] = it.key(); + else + quickSettings[type] << it.key(); + } + } + auto insertQuickSetting = [ quickSettings, this ](QuickSettingItem::QuickItemStyle type, int &row, int &column) { + if (!quickSettings.contains(type)) + return; + + int usedColumn = (type == QuickSettingItem::QuickItemStyle::Larger ? 2 : 1); + QList quickPlugins = quickSettings[type]; + for (QuickSettingItem *quickItem : quickPlugins) { + quickItem->setVisible(true); + m_pluginLayout->addWidget(quickItem, row, column, 1, usedColumn); + column += usedColumn; + if (column >= COLUMNCOUNT) { + row++; + column = 0; + } + } + }; + + int row = 0; + int column = 0; + insertQuickSetting(QuickSettingItem::QuickItemStyle::Larger, row, column); + insertQuickSetting(QuickSettingItem::QuickItemStyle::Standard, row, column); +} + +void QuickSettingContainer::updateFullItemLayout() +{ + while (m_componentWidget->layout()->count() > 0) + m_componentWidget->layout()->takeAt(0); + + QList fullItems; + QMap fullItemOrder; + for (QuickSettingItem *item : m_quickSettings) { + if (item->type() != QuickSettingItem::QuickItemStyle::Line) + continue; + + fullItems << item; + int order = -1; + QJsonObject metaData = m_pluginController->metaData(item->pluginItem()); + if (metaData.contains("order")) + order = metaData.value("order").toInt(); + + fullItemOrder[item] = order; + } + + std::sort(fullItems.begin(), fullItems.end(), [ fullItemOrder, fullItems ](QuickSettingItem *item1, QuickSettingItem *item2) { + int order1 = fullItemOrder.value(item1, -1); + int order2 = fullItemOrder.value(item2, -1); + if (order1 == order2) { + // 如果两个值相等,就根据他们的加载顺序进行排序 + return fullItems.indexOf(item1) < fullItems.indexOf(item2); + } + if (order1 == -1) + return false; + if (order2 == -1) + return true; + + return order1 < order2; + }); + + for (QuickSettingItem *item : fullItems) { + item->setVisible(true); + m_componentWidget->layout()->addWidget(item); + } +} + +void QuickSettingContainer::initUi() +{ + m_mainlayout->setSpacing(ITEMSPACE); + m_mainlayout->setContentsMargins(ITEMSPACE, ITEMSPACE, ITEMSPACE, ITEMSPACE); + + m_pluginLayout->setContentsMargins(0, 0, 0, 0); + m_pluginLayout->setSpacing(ITEMSPACE); + m_pluginLayout->setAlignment(Qt::AlignLeft); + for (int i = 0; i < COLUMNCOUNT; i++) + m_pluginLayout->setColumnMinimumWidth(i, ITEMWIDTH); + + m_pluginWidget->setLayout(m_pluginLayout); + m_mainlayout->addWidget(m_pluginWidget); + + QVBoxLayout *ctrlLayout = new QVBoxLayout(m_componentWidget); + ctrlLayout->setContentsMargins(0, 0, 0, 0); + ctrlLayout->setSpacing(ITEMSPACE); + ctrlLayout->setDirection(QBoxLayout::BottomToTop); + + m_mainlayout->addWidget(m_componentWidget); + // 加载所有的可以在快捷面板显示的插件 + QList plugins = m_pluginController->currentPlugins(); + for (PluginsItemInterface *plugin : plugins) { + appendPlugin(plugin, m_pluginController->itemKey(plugin), false); + } + + m_switchLayout->addWidget(m_mainWidget); + m_switchLayout->addWidget(m_childPage); + + setMouseTracking(true); + setAcceptDrops(true); + + QMetaObject::invokeMethod(this, [ = ] { + if (plugins.size() > 0) { + updateItemLayout(); + updateFullItemLayout(); + } + // 设置当前窗口的大小 + onResizeView(); + setFixedWidth(ITEMWIDTH * 4 + (ITEMSPACE * 5)); + }, Qt::QueuedConnection); +} + +void QuickSettingContainer::initConnection() +{ + connect(m_pluginController, &DockPluginController::pluginInserted, this, [ = ](PluginsItemInterface *itemInter, QString itemKey) { + appendPlugin(itemInter, itemKey); + }); + connect(m_pluginController, &DockPluginController::pluginRemoved, this, &QuickSettingContainer::onPluginRemove); + connect(m_pluginController, &DockPluginController::pluginUpdated, this, &QuickSettingContainer::onPluginUpdated); + + connect(m_childPage, &PluginChildPage::back, this, [ this ] { + showPage(m_mainWidget); + }); +} + +// 调整尺寸 +void QuickSettingContainer::onResizeView() +{ + if (m_switchLayout->currentWidget() == m_mainWidget) { + int selfPluginCount = 0; + int fullItemHeight = 0; + int widgetCount = 0; + for (QuickSettingItem *item : m_quickSettings) { + item->setFixedHeight(ITEMHEIGHT); + if (item->type() == QuickSettingItem::QuickItemStyle::Line) { + fullItemHeight += item->height(); + widgetCount++; + continue; + } + // 如果是置顶的插件,则认为它占用两个普通插件的位置 + int increCount = (item->type() == QuickSettingItem::QuickItemStyle::Larger ? 2 : 1); + selfPluginCount += increCount; + } + + int rowCount = selfPluginCount / COLUMNCOUNT; + if (selfPluginCount % COLUMNCOUNT > 0) + rowCount++; + + m_pluginWidget->setFixedHeight(ITEMHEIGHT * rowCount + ITEMSPACE * (rowCount - 1)); + m_componentWidget->setFixedHeight(fullItemHeight + (widgetCount - 1) * ITEMSPACE); + + setFixedHeight(ITEMSPACE * 3 + m_pluginWidget->height() + m_componentWidget->height()); + } else if (m_switchLayout->currentWidget() == m_childPage) { + setFixedHeight(m_childPage->height()); + } +} + +void QuickSettingContainer::onPluginUpdated(PluginsItemInterface *itemInter, const DockPart dockPart) +{ + if (dockPart != DockPart::QuickPanel) + return; + + for (QuickSettingItem *settingItem : m_quickSettings) { + if (settingItem->pluginItem() != itemInter) + continue; + + settingItem->doUpdate(); + break; + } +} diff --git a/plugins/pluginmanager/quicksettingcontainer.h b/plugins/pluginmanager/quicksettingcontainer.h new file mode 100644 index 000000000..cf7ed03cc --- /dev/null +++ b/plugins/pluginmanager/quicksettingcontainer.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 QUICKSETTINGCONTAINER_H +#define QUICKSETTINGCONTAINER_H + +#include "pluginproxyinterface.h" + +#include "dtkwidget_global.h" + +#include + +#include + +class DockItem; +class QVBoxLayout; +class DockPluginController; +class BrightnessModel; +class BrightnessWidget; +class QuickSettingItem; +class QStackedLayout; +class VolumeDevicesWidget; +class QLabel; +class PluginChildPage; +class QGridLayout; +class DisplaySettingWidget; +struct QuickDragInfo; + +DWIDGET_USE_NAMESPACE + +class QuickSettingContainer : public QWidget +{ + Q_OBJECT + +public: + void showPage(QWidget *widget, PluginsItemInterface *pluginInter = nullptr); + explicit QuickSettingContainer(DockPluginController *pluginController, QWidget *parent = nullptr); + ~QuickSettingContainer() override; + +protected: + void mouseMoveEvent(QMouseEvent *event) override; + bool eventFilter(QObject *watched, QEvent *event) override; + void showEvent(QShowEvent *event) override; + +private Q_SLOTS: + void onPluginRemove(PluginsItemInterface *itemInter); + void onShowChildWidget(QWidget *childWidget); + void onResizeView(); + void onPluginUpdated(PluginsItemInterface *itemInter, const DockPart dockPart); + +private: + // 加载UI + void initUi(); + // 初始化槽函数 + void initConnection(); + // 调整控件位置 + void updateItemLayout(); + // 调整全列插件的位置 + void updateFullItemLayout(); + // 插入插件 + void appendPlugin(PluginsItemInterface *itemInter, QString itemKey, bool needLayout = true); + +private: + QStackedLayout *m_switchLayout; + QWidget *m_mainWidget; + QWidget *m_pluginWidget; + QGridLayout *m_pluginLayout; + QWidget *m_componentWidget; + QVBoxLayout *m_mainlayout; + DockPluginController *m_pluginController; + PluginChildPage *m_childPage; + QuickDragInfo *m_dragInfo; + QList m_quickSettings; + PluginsItemInterface *m_childShowPlugin; +}; + +#endif // PLUGINCONTAINER_H diff --git a/plugins/pluginmanager/quicksettingitem.cpp b/plugins/pluginmanager/quicksettingitem.cpp new file mode 100644 index 000000000..2f672f2ee --- /dev/null +++ b/plugins/pluginmanager/quicksettingitem.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 "quicksettingitem.h" +#include "pluginsiteminterface.h" +#include "imageutil.h" +#include "largerquickitem.h" +#include "standardquickitem.h" +#include "linequickitem.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define ICONWIDTH 24 +#define ICONHEIGHT 24 +#define ICONSPACE 10 +#define RADIUS 8 +#define FONTSIZE 10 + +#define BGWIDTH 128 +#define BGSIZE 36 +#define MARGINLEFTSPACE 10 +#define OPENICONSIZE 12 +#define MARGINRIGHTSPACE 9 + +DWIDGET_USE_NAMESPACE + +QuickSettingItem::QuickSettingItem(PluginsItemInterface *const pluginInter, const QString &itemKey, QWidget *parent) + : QWidget(parent) + , m_pluginInter(pluginInter) + , m_itemKey(itemKey) +{ + setAcceptDrops(true); + this->installEventFilter(this); +} + +QuickSettingItem::~QuickSettingItem() +{ +} + +PluginsItemInterface *QuickSettingItem::pluginItem() const +{ + return m_pluginInter; +} + +const QPixmap QuickSettingItem::dragPixmap() +{ + QPixmap pluginPixmap = m_pluginInter->icon(DockPart::QuickPanel, DGuiApplicationHelper::instance()->themeType()).pixmap(ICONWIDTH, ICONHEIGHT); + + QPainter foregroundPainter(&pluginPixmap); + foregroundPainter.setPen(foregroundColor()); + foregroundPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + foregroundPainter.fillRect(pluginPixmap.rect(), foregroundColor()); + + QPixmap pixmapDrag(ICONWIDTH + ICONSPACE + FONTSIZE * 2, ICONHEIGHT + ICONSPACE + FONTSIZE * 2); + pixmapDrag.fill(Qt::transparent); + QPainter painter(&pixmapDrag); + painter.drawPixmap(QPoint((ICONSPACE + FONTSIZE * 2) / 2, 0), pluginPixmap); + painter.setPen(foregroundPainter.pen()); + + QFont font; + font.setPixelSize(FONTSIZE); + painter.setFont(font); + QTextOption option; + option.setAlignment(Qt::AlignTop | Qt::AlignHCenter); + painter.drawText(QRect(QPoint(0, ICONHEIGHT + ICONSPACE), + QPoint(pixmapDrag.width(), pixmapDrag.height())), m_pluginInter->pluginDisplayName(), option); + return pixmapDrag; +} + +const QString QuickSettingItem::itemKey() const +{ + return m_itemKey; +} + +void QuickSettingItem::paintEvent(QPaintEvent *e) +{ + QWidget::paintEvent(e); + QPainter painter(this); + painter.setRenderHint(QPainter::RenderHint::Antialiasing); + painter.setPen(foregroundColor()); + QPainterPath path; + path.addRoundedRect(rect(), RADIUS, RADIUS); + painter.setClipPath(path); + // 绘制背景色 + QColor backColor(Qt::white); + if (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::ColorType::DarkType) { + backColor = Qt::black; + backColor.setAlphaF(0.5); + } + DPalette dpa = DPaletteHelper::instance()->palette(this); + painter.fillRect(rect(), backColor); +} + +QColor QuickSettingItem::foregroundColor() const +{ + DPalette dpa = DPaletteHelper::instance()->palette(this); + // TODO: 此处的颜色是临时获取的,后期需要和设计师确认,改成正规的颜色 + if (m_pluginInter->status() == PluginsItemInterface::PluginMode::Active) + return dpa.color(DPalette::ColorGroup::Active, DPalette::ColorRole::Text); + + if (m_pluginInter->status() == PluginsItemInterface::PluginMode::Deactive) + return dpa.color(DPalette::ColorGroup::Disabled, DPalette::ColorRole::Text); + + return dpa.color(DPalette::ColorGroup::Normal, DPalette::ColorRole::Text); +} + +QuickSettingItem *QuickSettingFactory::createQuickWidget(PluginsItemInterface * const pluginInter, const QString &itemKey) +{ + // 如果显示在面板的图标或者Widget为空,则不让显示(例如电池插件) + if (!(pluginInter->flags() & PluginFlag::Type_Common)) + return nullptr; + + if (pluginInter->flags() & PluginFlag::Quick_Multi) + return new LargerQuickItem(pluginInter, itemKey); + + if (pluginInter->flags() & PluginFlag::Quick_Full) + return new LineQuickItem(pluginInter, itemKey); + + if (pluginInter->flags() & PluginFlag::Quick_Single) + return new StandardQuickItem(pluginInter, itemKey); + + return nullptr; +} diff --git a/plugins/pluginmanager/quicksettingitem.h b/plugins/pluginmanager/quicksettingitem.h new file mode 100644 index 000000000..53e192256 --- /dev/null +++ b/plugins/pluginmanager/quicksettingitem.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 QUICKSETTINGITEM_H +#define QUICKSETTINGITEM_H + +#include + +class PluginsItemInterface; +class QuickIconWidget; + +class QuickSettingItem : public QWidget +{ + Q_OBJECT + +public: + enum class QuickItemStyle { + Standard = 1, // 插件的UI显示单列 + Larger, // 插件的UI显示双列,例如网络和蓝牙等 + Line // 插件的UI整行显示,例如声音,亮度、音乐播放等 + }; + +public: + QuickSettingItem(PluginsItemInterface *const pluginInter, const QString &itemKey, QWidget *parent = nullptr); + ~QuickSettingItem() override; + PluginsItemInterface *pluginItem() const; + virtual const QPixmap dragPixmap(); + virtual void doUpdate() {} + virtual void detachPlugin() {} + const QString itemKey() const; + + virtual QuickItemStyle type() const = 0; + +Q_SIGNALS: + void requestShowChildWidget(QWidget *); + +protected: + void paintEvent(QPaintEvent *e) override; + QColor foregroundColor() const; + +private: + PluginsItemInterface *m_pluginInter; + QString m_itemKey; +}; + +class QuickSettingFactory +{ +public: + static QuickSettingItem *createQuickWidget(PluginsItemInterface *const pluginInter, const QString &itemKey); +}; + +#endif // QUICKSETTINGITEM_H diff --git a/plugins/pluginmanager/resources/dock_control.dci b/plugins/pluginmanager/resources/dock_control.dci new file mode 100644 index 0000000000000000000000000000000000000000..38848da3b7a9e524bdef6b868a5d74784234c883 GIT binary patch literal 767 zcmZ>9_GDmWWME)2G-IFytY?DgX3EPi%1zACOGzxsCda(BP)$t6bacE^o$aHV||#}Fi(TUNirUyxCj^lAV8_7wV>uO!W;%M zmSpk~R7a4fo7*iQJKWVNAS}Sb=PZz|uYZ7B;Jm^A&-VYbq&>4Hzqdb9?ZJ6KmXpKz zAoD^+hJzjs3Ig&i>`cst2Lmi3JSU}OmCw)p@;CpNK}B7?;NSEI+a@l0He=z>mY+U9 zDy<#QTzO_^z07#Z8BSv{i-sl{=ZqIh=ew;K6sI~Gn_bhn!S-}wv3;^$5hl2nB literal 0 HcmV?d00001 diff --git a/plugins/pluginmanager/standardquickitem.cpp b/plugins/pluginmanager/standardquickitem.cpp new file mode 100644 index 000000000..c38dc016f --- /dev/null +++ b/plugins/pluginmanager/standardquickitem.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 "standardquickitem.h" +#include "pluginsiteminterface.h" + +#include +#include + +#include +#include +#include + +#define ICONHEIGHT 24 +#define ICONWIDTH 24 +#define TEXTHEIGHT 11 + +DWIDGET_USE_NAMESPACE + +StandardQuickItem::StandardQuickItem(PluginsItemInterface *const pluginInter, const QString &itemKey, QWidget *parent) + : QuickSettingItem(pluginInter, itemKey, parent) + , m_itemParentWidget(nullptr) + , m_needPaint(true) +{ + initUi(); +} + +StandardQuickItem::~StandardQuickItem() +{ +} + +QuickSettingItem::QuickItemStyle StandardQuickItem::type() const +{ + return QuickSettingItem::QuickItemStyle::Standard; +} + +void StandardQuickItem::mouseReleaseEvent(QMouseEvent *event) +{ + Q_UNUSED(event); + QStringList commandArgument = pluginItem()->itemCommand(itemKey()).split(" "); + if (commandArgument.size() > 0) { + QString command = commandArgument.first(); + commandArgument.removeFirst(); + QProcess::startDetached(command, commandArgument); + } +} + +void StandardQuickItem::resizeEvent(QResizeEvent *event) +{ + doUpdate(); + QuickSettingItem::resizeEvent(event); +} + +void StandardQuickItem::initUi() +{ + QWidget *topWidget = iconWidget(this); + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(topWidget); + installEventFilter(this); +} + +QWidget *StandardQuickItem::iconWidget(QWidget *parent) +{ + // 显示图标的窗体 + QWidget *widget = new QWidget(parent); + m_needPaint = true; + QIcon icon = pluginItem()->icon(DockPart::QuickPanel); + if (icon.isNull()) { + // 如果图标为空,则将获取itemWidget作为它的显示 + QWidget *itemWidget = pluginItem()->itemWidget(QUICK_ITEM_KEY); + if (itemWidget) { + m_itemParentWidget = itemWidget->parentWidget(); + QHBoxLayout *layout = new QHBoxLayout(widget); + layout->setContentsMargins(0, 0, 0 ,0); + itemWidget->setParent(widget); + layout->addWidget(itemWidget); + itemWidget->setVisible(true); + m_needPaint = false; + } + } + + if (m_needPaint) { + // 如果没有子窗体,则需要添加下方的文字 + QVBoxLayout *layout = new QVBoxLayout(widget); + layout->setAlignment(Qt::AlignVCenter); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + QLabel *imageLabel = new QLabel(widget); + imageLabel->setObjectName("imageLabel"); + imageLabel->setFixedHeight(ICONHEIGHT); + imageLabel->setAlignment(Qt::AlignCenter); + + QLabel *labelText = new QLabel(widget); + labelText->setObjectName("textLabel"); + labelText->setFixedHeight(TEXTHEIGHT); + updatePluginName(labelText); + labelText->setAlignment(Qt::AlignCenter); + labelText->setFont(DFontSizeManager::instance()->t10()); + layout->addWidget(imageLabel); + layout->addSpacing(7); + layout->addWidget(labelText); + } + + return widget; +} + +QPixmap StandardQuickItem::pixmap() const +{ + // 如果快捷面板区域的图标为空,那么就获取itemWidget的截图 + QIcon icon = pluginItem()->icon(DockPart::QuickPanel); + if (icon.isNull()) + return QPixmap(); + + int pixmapWidth = ICONWIDTH; + int pixmapHeight = ICONHEIGHT; + QList iconSizes = icon.availableSizes(); + if (iconSizes.size() > 0) { + QSize size = iconSizes[0]; + if (size.isValid() && !size.isEmpty() && !size.isNull()) { + pixmapWidth = size.width(); + pixmapHeight = size.height(); + } + } + + return icon.pixmap(pixmapWidth / qApp->devicePixelRatio(), pixmapHeight / qApp->devicePixelRatio()); +} + +QLabel *StandardQuickItem::findChildLabel(QWidget *parent, const QString &childObjectName) const +{ + QList childrends = parent->children(); + for (QObject *child : childrends) { + QWidget *widget = qobject_cast(child); + if (!widget) + continue; + + QLabel *label = qobject_cast(child); + if (label && widget->objectName() == childObjectName) + return label; + + label = findChildLabel(widget, childObjectName); + if (label) + return label; + } + + return nullptr; +} + +void StandardQuickItem::updatePluginName(QLabel *textLabel) +{ + if (!textLabel) + return; + + QString text = pluginItem()->description(); + if (text.isEmpty()) + text = pluginItem()->pluginDisplayName(); + QFontMetrics ftm(textLabel->font()); + text = ftm.elidedText(text, Qt::TextElideMode::ElideRight, textLabel->width()); + textLabel->setText(text); +} + +void StandardQuickItem::doUpdate() +{ + if (m_needPaint) { + QLabel *imageLabel = findChildLabel(this, "imageLabel"); + if (imageLabel) { + // 更新图像 + imageLabel->setPixmap(pixmap()); + } + updatePluginName(findChildLabel(this, "textLabel")); + } else { + QWidget *itemWidget = pluginItem()->itemWidget(QUICK_ITEM_KEY); + if (itemWidget) + itemWidget->update(); + } +} + +void StandardQuickItem::detachPlugin() +{ + QWidget *itemWidget = pluginItem()->itemWidget(QUICK_ITEM_KEY); + if (itemWidget && !m_needPaint) + itemWidget->setParent(m_itemParentWidget); +} diff --git a/plugins/pluginmanager/standardquickitem.h b/plugins/pluginmanager/standardquickitem.h new file mode 100644 index 000000000..74cff3b9d --- /dev/null +++ b/plugins/pluginmanager/standardquickitem.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 ~ 2022 Deepin Technology Co., Ltd. + * + * Author: donghualin + * + * Maintainer: donghualin + * + * 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 + * 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 STANDARDQUICKITEM_H +#define STANDARDQUICKITEM_H + +#include "quicksettingitem.h" + +class QLabel; + +// 插件在快捷面板中展示的样式,这个为默认,展示一行一列的那种 +class StandardQuickItem : public QuickSettingItem +{ + Q_OBJECT + +public: + StandardQuickItem(PluginsItemInterface *const pluginInter, const QString &itemKey, QWidget *parent = nullptr); + ~StandardQuickItem() override; + + QuickItemStyle type() const override; + void doUpdate() override; + void detachPlugin() override; + +protected: + void mouseReleaseEvent(QMouseEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + +private: + void initUi(); + QWidget *iconWidget(QWidget *parent); + QPixmap pixmap() const; + QLabel *findChildLabel(QWidget *parent, const QString &childObjectName) const; + void updatePluginName(QLabel *textLabel); + +private: + QWidget *m_itemParentWidget; + bool m_needPaint; +}; + +#endif // SINGLEQUICKITEM_H