Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/plugin/API_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,19 @@ class DS_SHARE DPluginMetaData : public QObject
};
```

### Applet Enable Configuration

`AppletConfigManager` in the `dde-shell` process reads `org.deepin.dde.shell / org.deepin.dde.shell / enable`, uses the applet's `Plugin.Id` as the DConfig subpath, and passes the enabled state to `DAppletLoader`. This configuration applies only to child applets that declare `Plugin.Parent`; top-level panels such as the Dock are always created by the startup flow and are not controlled by DConfig. When the value is `false`, the corresponding child applet instance is not created. Runtime configuration changes unload or reload that child applet immediately. `dde-shell-frame` does not read or store this configuration state.

The `-d` command-line option is handled by `DPluginLoader`, which filters the corresponding applet, including a top-level panel, while scanning metadata. It has higher priority than DConfig; an applet disabled with `-d` cannot be re-enabled through DConfig during the lifetime of the process.

For example, to disable the launchpad applet:

```bash
dde-dconfig set -a org.deepin.dde.shell -r org.deepin.dde.shell \
-s /org.deepin.ds.dock.launcherapplet -k enable -v false
```

### DAppletBridge API

Bridge for communicating with applet from C++:
Expand Down
13 changes: 13 additions & 0 deletions docs/plugin/API_zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,19 @@ class DS_SHARE DPluginMetaData : public QObject
};
```

### Applet 启用配置

`dde-shell` 进程中的 `AppletConfigManager` 读取 `org.deepin.dde.shell / org.deepin.dde.shell / enable`,并使用 Applet 的 `Plugin.Id` 作为 DConfig subpath,再将启用状态交给 `DAppletLoader`。该配置只作用于包含 `Plugin.Parent` 的子 Applet;Dock 等顶层 Panel 始终由启动流程创建,不受 DConfig 控制。值为 `false` 时不会创建对应的子 Applet 实例;运行时修改配置会实时卸载或重新加载对应子 Applet。`dde-shell-frame` 本身不读取或保存此配置状态。

命令行参数 `-d` 由 `DPluginLoader` 处理,会在扫描 metadata 时直接过滤对应 Applet,包括顶层 Panel。它的优先级高于 DConfig;在当前进程生命周期内,被 `-d` 禁用的 Applet 无法通过 DConfig 重新启用。

例如禁用启动器 Applet:

```bash
dde-dconfig set -a org.deepin.dde.shell -r org.deepin.dde.shell \
-s /org.deepin.ds.dock.launcherapplet -k enable -v false
```

### DAppletBridge API

从 C++ 与小部件通信的桥接:
Expand Down
2 changes: 2 additions & 0 deletions shell/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ endif()

add_executable(dde-shell
main.cpp
appletconfigmanager.h
appletconfigmanager.cpp
appletloader.h
appletloader.cpp
shell.h
Expand Down
89 changes: 89 additions & 0 deletions shell/appletconfigmanager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "appletconfigmanager.h"

#include "pluginloader.h"

Check warning on line 7 in shell/appletconfigmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "pluginloader.h" not found.

#include <DConfig>

Check warning on line 9 in shell/appletconfigmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <DConfig> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QLoggingCategory>

Check warning on line 10 in shell/appletconfigmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QLoggingCategory> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QSet>

Check warning on line 11 in shell/appletconfigmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QSet> not found. Please note: Cppcheck does not need standard library headers to get proper results.

DS_BEGIN_NAMESPACE

DCORE_USE_NAMESPACE

Q_DECLARE_LOGGING_CATEGORY(dsLoaderLog)

static constexpr auto AppId{"org.deepin.dde.shell"};
static constexpr auto ConfigName{"org.deepin.dde.shell"};
static constexpr auto EnableKey{"enable"};

AppletConfigManager::AppletConfigManager(QObject *parent)
: QObject(parent)
{
}

QStringList AppletConfigManager::disabledPlugins() const

Check warning on line 28 in shell/appletconfigmanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

The function 'disabledPlugins' is never used.
{
return m_disabledPlugins;
}

bool AppletConfigManager::isAppletEnabled(const QString &pluginId) const
{
return !m_disabledPlugins.contains(pluginId);
}

void AppletConfigManager::setAppletEnabled(const QString &pluginId, bool enabled)
{
const bool currentlyEnabled = isAppletEnabled(pluginId);
if (currentlyEnabled == enabled)
return;

if (enabled)
m_disabledPlugins.removeAll(pluginId);
else
m_disabledPlugins.append(pluginId);
Q_EMIT appletEnabledChanged(pluginId, enabled);
}

void AppletConfigManager::ensureAppletConfigs()
{
auto loader = DPluginLoader::instance();
const auto plugins = loader->plugins();
QSet<QString> pluginIds;
for (const auto &plugin : plugins) {
// Top-level panels/applets are created by AppletManager and are not
// controlled by DConfig. Only child applets can be toggled at runtime.
if (plugin.value(QLatin1String("Parent")).toString().isEmpty())
continue;
pluginIds.insert(plugin.pluginId());
}

for (const auto &pluginId : pluginIds) {
if (m_appletConfigs.contains(pluginId))
continue;
if (!isAppletEnabled(pluginId))
continue;

auto config = DConfig::create(QLatin1String(AppId),
QLatin1String(ConfigName),
QLatin1Char('/') + pluginId,
this);
m_appletConfigs.insert(pluginId, config);
if (!config || !config->isValid()) {
qCWarning(dsLoaderLog) << "Unable to create applet DConfig; applet remains enabled:" << pluginId;
setAppletEnabled(pluginId, true);
continue;
}

setAppletEnabled(pluginId, config->value(QLatin1String(EnableKey), true).toBool());
QObject::connect(config, &DConfig::valueChanged, this, [this, pluginId, config](const QString &key) {
if (key == QLatin1String(EnableKey))
setAppletEnabled(pluginId, config->value(QLatin1String(EnableKey), true).toBool());
});
}
}

DS_END_NAMESPACE
39 changes: 39 additions & 0 deletions shell/appletconfigmanager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "dsglobal.h"

Check warning on line 7 in shell/appletconfigmanager.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "dsglobal.h" not found.

#include <QObject>

Check warning on line 9 in shell/appletconfigmanager.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QObject> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QMap>

Check warning on line 10 in shell/appletconfigmanager.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QMap> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QStringList>

Check warning on line 11 in shell/appletconfigmanager.h

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QStringList> not found. Please note: Cppcheck does not need standard library headers to get proper results.

namespace Dtk::Core {
class DConfig;
}

DS_BEGIN_NAMESPACE

class AppletConfigManager : public QObject
{
Q_OBJECT
public:
explicit AppletConfigManager(QObject *parent = nullptr);

QStringList disabledPlugins() const;
bool isAppletEnabled(const QString &pluginId) const;
void ensureAppletConfigs();

Q_SIGNALS:
void appletEnabledChanged(const QString &pluginId, bool enabled);

private:
void setAppletEnabled(const QString &pluginId, bool enabled);

QMap<QString, Dtk::Core::DConfig *> m_appletConfigs;
QStringList m_disabledPlugins;
};

DS_END_NAMESPACE
105 changes: 97 additions & 8 deletions shell/appletloader.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2023 -2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "appletloader.h"
#include "appletconfigmanager.h"
#include "pluginloader.h"

Check warning on line 7 in shell/appletloader.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "pluginloader.h" not found.
#include "applet.h"
#include "containment.h"
#include "appletdata.h"
Expand Down Expand Up @@ -84,22 +85,35 @@
bool init(DApplet *applet);

void createChildren(DApplet *applet);
void handleAppletEnabledChanged(const QString &pluginId, bool enabled);
void createEnabledApplets(DApplet *applet, const QString &pluginId);
void removeDisabledApplets(DApplet *applet, const QString &pluginId);

void loadTranslation(const DPluginMetaData &pluginData);
void removeTranslation(const QString &pluginId);
void removeTranslations(const QString &pluginId);

QPointer<DApplet> m_applet = nullptr;
QPointer<AppletConfigManager> m_configManager = nullptr;
QMap<QString, QTranslator *> m_pluginTranslators;

D_DECLARE_PUBLIC(DAppletLoader);
};

DAppletLoader::DAppletLoader(class DApplet *applet, QObject *parent)
DAppletLoader::DAppletLoader(DApplet *applet, AppletConfigManager *configManager, QObject *parent)
: QObject(parent)
, DObject(*new DAppletLoaderPrivate(this))
{
D_D(DAppletLoader);
d->m_applet = applet;
d->m_configManager = configManager;
Q_ASSERT(configManager);
QObject::connect(configManager,
&AppletConfigManager::appletEnabledChanged,
this,
[d](const QString &pluginId, bool enabled) {
d->handleAppletEnabledChanged(pluginId, enabled);
});
}

DAppletLoader::~DAppletLoader()
Expand All @@ -115,10 +129,10 @@
if (!d->load(d->m_applet))
return;

d->createRootObject(d->m_applet);

if (!d->init(d->m_applet))
return;

d->createRootObject(d->m_applet);
}

DApplet *DAppletLoader::applet() const
Expand Down Expand Up @@ -146,10 +160,11 @@
}
});

qCDebug(dsLoaderLog) << "Begin to create rootObject the applet:" << applet->pluginId();
if (!engine->create()) {
engine->deleteLater();
}
QMetaObject::invokeMethod(engine, [engine]() {
if (!engine->create()) {
engine->deleteLater();
}
}, Qt::QueuedConnection);
}

bool DAppletLoaderPrivate::doLoad(DApplet *applet)
Expand Down Expand Up @@ -190,6 +205,8 @@
const auto data = applet->appletData();
auto groups = groupList(applet, data);
for (const auto &item : std::as_const(groups)) {
if (!m_configManager->isAppletEnabled(item.pluginId()))
continue;

auto child = containment->createApplet(item);
if (!child) {
Expand All @@ -199,6 +216,67 @@
}
}

void DAppletLoaderPrivate::handleAppletEnabledChanged(const QString &pluginId, bool enabled)
{
if (!m_applet)
return;

if (enabled) {
createEnabledApplets(m_applet, pluginId);
return;
}

removeDisabledApplets(m_applet, pluginId);
removeTranslations(pluginId);
}

void DAppletLoaderPrivate::createEnabledApplets(DApplet *applet, const QString &pluginId)
{
auto containment = qobject_cast<DContainment *>(applet);
if (!containment)
return;

const auto groups = groupList(applet, applet->appletData());
for (const auto &data : groups) {
if (data.pluginId() != pluginId)
continue;

auto child = containment->createApplet(data);
if (!child)
continue;

loadTranslation(child->pluginMetaData());
if (!load(child) || !init(child)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

之前不是有这个流程么,再写一个,之前的不能复用么,

removeTranslations(pluginId);
continue;
}
createRootObject(child);
}

const auto children = containment->applets();
for (auto child : children) {
if (child->pluginId() == pluginId)
continue;
createEnabledApplets(child, pluginId);
}
}

void DAppletLoaderPrivate::removeDisabledApplets(DApplet *applet, const QString &pluginId)
{
auto containment = qobject_cast<DContainment *>(applet);
if (!containment)
return;

const auto children = containment->applets();
for (auto child : children) {
if (child->pluginId() == pluginId) {
containment->removeApplet(child);
continue;
}
removeDisabledApplets(child, pluginId);
}
}

bool DAppletLoaderPrivate::load(DApplet *applet)
{
if (!doLoad(applet)) {
Expand Down Expand Up @@ -248,6 +326,9 @@
const QString baseDir = pluginData.pluginDir();
const QString pluginId = pluginData.pluginId();

if (!m_configManager->isAppletEnabled(pluginId))
return;

auto translator = new QTranslator(qApp);
const QString pluginTranslationDir(baseDir + "/translations/");
if (translator->load(QLocale::system(), pluginId, QLatin1String("_"), pluginTranslationDir)) {
Expand Down Expand Up @@ -275,4 +356,12 @@
}
}

void DAppletLoaderPrivate::removeTranslations(const QString &pluginId)
{
const auto children = DPluginLoader::instance()->childrenPlugin(pluginId);
for (const auto &child : children)
removeTranslations(child.pluginId());
removeTranslation(pluginId);
}

DS_END_NAMESPACE
5 changes: 3 additions & 2 deletions shell/appletloader.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2023 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

Expand All @@ -12,13 +12,14 @@
DS_BEGIN_NAMESPACE

class DApplet;
class AppletConfigManager;
class DAppletLoaderPrivate;
class DAppletLoader : public QObject, public DTK_CORE_NAMESPACE::DObject
{
Q_OBJECT
D_DECLARE_PRIVATE(DAppletLoader)
public:
explicit DAppletLoader(DApplet *applet, QObject *parent = nullptr);
explicit DAppletLoader(DApplet *applet, AppletConfigManager *configManager, QObject *parent = nullptr);
virtual ~DAppletLoader() override;

void exec();
Expand Down
Loading
Loading