Skip to content

feat: add applet visibility control to DAppletItemModel and - #1676

Open
wjyrich wants to merge 1 commit into
linuxdeepin:masterfrom
wjyrich:task-393299-1
Open

feat: add applet visibility control to DAppletItemModel and#1676
wjyrich wants to merge 1 commit into
linuxdeepin:masterfrom
wjyrich:task-393299-1

Conversation

@wjyrich

@wjyrich wjyrich commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

DPluginLoader

Implement a framework-level hidden applets mechanism that allows suppressing individual applet root objects from the containment model without destroying them. The DAppletItemModel now maintains both a complete root objects list and a filtered visible subset. A new setRootObjectVisible method enables toggling visibility while preserving the original insertion order upon restoration. The DPluginLoader exposes hiddenApplets, isAppletHidden, and setHiddenApplets APIs, and the shell synchronizes these with a DConfig key hiddenApplets. Hidden applets remain loaded but their root objects are reparented to the applet and removed from the visual model. Unit tests verify order preservation and removal semantics.

Log: Added applet visibility control API and DConfig integration

Influence:

  1. Test applet hiding and showing via DConfig changes
  2. Verify that hidden applets remain loaded and their root objects are properly reparented
  3. Test that removing a plugin ID from the hidden list restores the applet in its original model order
  4. Verify that the model signals emit correctly on visibility changes
  5. Test interaction with applet creation, deletion, and root object changes
  6. Verify that the API works for multiple containments simultaneously

feat: 为 DAppletItemModel 和 DPluginLoader 添加 Applet 显隐控制

实现框架级隐藏 Applet 机制,可在不销毁实例的前提下将其根对象从容
器模型中移除。DAppletItemModel 维护完整的根对象列表和可见子集,
新增 setRootObjectVisible 方法支持按需切换显隐,恢复时保持原有
插入顺序。DPluginLoader 提供 hiddenAppletsisAppletHiddensetHiddenApplets 接口,Shell 通过 DConfig 键 hiddenApplets 同步配 置。隐藏的 Applet 仍保持加载,其根对象重新挂载到 Applet 自身并从视觉模型
中移除。单元测试验证了顺序保持和移除语义。

Log: 新增 Applet 显隐控制接口和 DConfig 集成

Influence:

  1. 通过 DConfig 变更测试 Applet 的隐藏和显示
  2. 验证隐藏的 Applet 依然保持加载,根对象正确重新挂载
  3. 测试从隐藏列表中移除插件 ID 后 Applet 按原有顺序恢复
  4. 验证模型信号在可见性变更时正确发出
  5. 测试与 Applet 创建、删除及根对象变更的交互
  6. 验证该 API 能同时作用于多个容器

PMS: TASK-393299

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @wjyrich, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: wjyrich

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Comment thread shell/shell.cpp Outdated
}

if (m_dconfig && m_dconfig->isValid()) {
connect(m_dconfig, &Dtk::Core::DConfig::valueChanged,

@18202781743 18202781743 Jul 28, 2026

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.

配置用subpath的方式去获取,而不是把所有插件都放在一个hiddenApplets里,每个插件都有一个单独的配置,方便后面override, https://github.com/18202781743/cskills/blob/4745ee77b82d808182f2a8681db135b0933fb2ba/dtk-development/references/config/concepts.md?plain=1#L74

Comment thread frame/appletitemmodel.cpp
{
}

QList<QObject *> DAppletItemModel::rootObjects() const

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.

这里rootObjects要改成visibleRootObjects么,要不要加个新的visibleRootObjects属性,

Comment thread frame/appletitemmodel.h Outdated

QList<QObject *> rootObjects() const;
void append(QObject *rootObject);
void append(QObject *rootObject, bool visible = true);

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.

这个是公共接口,不能直接加,另外也不需要,在添加的地方去拦截比较合适,

@wjyrich
wjyrich force-pushed the task-393299-1 branch 2 times, most recently from f159ac7 to c07f118 Compare July 28, 2026 06:39
Comment thread docs/plugin/API_en.md Outdated
Q_PROPERTY(QString id READ id CONSTANT FINAL)
Q_PROPERTY(QString pluginId READ pluginId CONSTANT FINAL)
Q_PROPERTY(QObject *rootObject READ rootObject NOTIFY rootObjectChanged)
Q_PROPERTY(bool enable READ enable WRITE setEnable NOTIFY enableChanged FINAL)

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.

这个是不是不需要呀,disable了这个applet都不会有了,

@wjyrich
wjyrich force-pushed the task-393299-1 branch 8 times, most recently from ba4eb47 to dccc169 Compare July 29, 2026 03:31
Comment thread frame/pluginloader.cpp
Q_EMIT appletEnabledChanged(pluginId, enabled);
}

DApplet *DPluginLoader::loadApplet(const DAppletData &data)

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.

如果m_disabledPlugins只是在这里使用的话,可以不用加在pluginloader.cpp里,放在dde-shell的appletloader.cpp里,

Comment thread frame/pluginloader.h
QStringList pluginDirs() const;
void setPluginDirs(const QStringList &dirs);

QStringList disabledApplets() const;

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.

不能删接口的,这个也不用删除吧,

@wjyrich
wjyrich force-pushed the task-393299-1 branch 2 times, most recently from 7e03ceb to e46752f Compare July 30, 2026 01:59
@wjyrich

wjyrich commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

/test github-pr-review-ci

@wjyrich
wjyrich force-pushed the task-393299-1 branch 5 times, most recently from fc66cf6 to c19da7e Compare July 30, 2026 04:58
Comment thread shell/appletconfigmanager.cpp Outdated
pluginIds.insert(plugin.pluginId());
}

const auto configuredPluginIds = m_appletConfigs.keys();

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.

这个刚开始不是空的么,不需要遍历吧,

Comment thread shell/main.cpp Outdated
void quit()
{
for (auto item : std::as_const(m_loaders)) {
const auto loaders = m_loaders;

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.

复制一份,有特殊含义么?另外,这里的m_loaders,应该针对的都是没有parent的applet,

Comment thread shell/appletloader.cpp
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.

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

Add the ability to enable or disable DApplet instances via DConfig,
controlling whether their root objects appear in the parent containment
model.

The new `enable` property is exposed as a Q_PROPERTY on DApplet, backed
by a DConfig key `enable` under `org.deepin.dde.shell`. When loading,
DApplet reads this config and watches for changes.

Disabled applets remain loaded but their root objects are removed from
the containment's `appletItems` model; re-enabling re-inserts them.
This enables runtime toggling of applet visibility without unloading/
reloading plugins.

Unit tests are added for the DAppletItemModel (append/remove) and the
enable property change notification logic.

Log: Added DApplet enable/disable feature via DConfig

Influence:
1. Test creating an applet and verifying its enable property is true
by default
2. Set enable to false via dde-dconfig and verify the applet root object
is removed from the containment model
3. Set enable to true and verify the root object reappears
4. Verify that setEnable with the same value does not emit enableChanged
signal
5. Test loading a root plugin (like dock) - its enable should not be
affected (skipped)
6. Verify DConfig changes are watched and applied dynamically at runtime
7. Run unit tests: EnablePropertyNotifiesOnlyOnChange,
AppendsRootObjects, RemovesRootObject

feat: 添加支持通过 DConfig 启用/禁用 Applet 的接口

新增通过 DConfig 控制 DApplet 实例启用的能力,决定其根对象是否显示在父容
器模型中。

新的 `enable` 属性作为 DApplet 的 Q_PROPERTY 暴露,由
`org.deepin.dde.shell` 下的 DConfig 键 `enable` 驱动。加载时 DApplet 读
取该配置并监听变化。

被禁用的 Applet 实例仍保持加载,但根对象从容器的 `appletItems` 模型中
移除;重新启用后会重新追加。这实现了无需卸载/重新加载插件即可运行时切换
Applet 可见性。

新增了 DAppletItemModel(追加/移除)以及 enable 属性变更通知逻辑的单元
测试。

Log: 新增 DApplet 启用/禁用功能

Influence:
1. 测试创建 Applet 并验证其 enable 属性默认为 true
2. 通过 dde-dconfig 将 enable 设为 false,验证根对象已从容器模型中移除
3. 设为 true 后验证根对象重新出现
4. 验证 setEnable 相同值时不会触发 enableChanged 信号
5. 测试加载根插件(如 dock),其 enable 不应受影响(跳过)
6. 验证 DConfig 变化能被监听并在运行时动态生效
7. 运行单元测试:EnablePropertyNotifiesOnlyOnChange、
AppendsRootObjects、RemovesRootObject

PMS: TASK-393299
@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

★ 总体评分:75分

■ 【总体评价】

代码实现了 Applet 的动态启用与禁用配置管理,但存在导致初始禁用插件无法被动态启用的严重逻辑缺陷。
逻辑设计存在盲区导致核心功能失效,但因无安全漏洞且代码结构规范保留及格分数。

■ 【详细分析】

  • 1.语法逻辑(存在严重错误)✕

AppletConfigManager::ensureAppletConfigs 函数中,当判断 if (!isAppletEnabled(pluginId)) 为真时直接 continue,导致初始状态被禁用的 Applet 不会创建 DConfig 对象及监听其 valueChanged 信号。
潜在问题:初始被禁用的 Applet 永远无法接收到配置变更信号,导致用户在运行时通过 DConfig 启用该 Applet 时无法触发 appletEnabledChanged 信号,动态启用功能彻底失效;removeDisabledApplets 在遍历 children 时移除元素,若 applets() 返回非副本可能引发迭代器失效。
建议:移除 ensureAppletConfigs 中的 if (!isAppletEnabled(pluginId)) continue; 判断,确保所有受控子 Applet 均建立 DConfig 监听;确认 applets() 返回值为副本以避免遍历修改冲突。

  • 2.代码质量(良好)✓

代码结构清晰,新增了完整的文档说明(中英文 API 文档)和单元测试,命名规范,注释详细解释了顶层 Panel 与子 Applet 的控制差异。
潜在问题:createEnabledApplets 中的递归逻辑在处理新创建的 containment 时,可能无法正确为其内部子 applet 建立加载流程。
建议:优化递归创建逻辑,确保新创建的 applet 如果作为 containment 时,其内部的子 applet 也能被正确初始化。

  • 3.代码性能(良好)✓

createEnabledAppletsremoveDisabledApplets 采用递归遍历树结构,在桌面环境的 Applet 树规模下性能开销可接受。doCreateRootObject 改为异步执行避免了阻塞主线程。
建议:无需重大修改,保持现状即可。

  • 4.代码安全(存在0个安全漏洞)✓

漏洞对比统计:新增漏洞 0 个,减少漏洞 0 个,持平 0 个
代码主要处理本地配置读取和对象树管理,未涉及外部输入解析、网络通信或特权操作,不存在可利用的安全风险。
建议:无需安全修复。

■ 【改进建议代码示例】

// 修复 appletconfigmanager.cpp 中的 ensureAppletConfigs 方法
void AppletConfigManager::ensureAppletConfigs()
{
    auto loader = DPluginLoader::instance();
    const auto plugins = loader->plugins();
    QSet<QString> pluginIds;
    for (const auto &plugin : plugins) {
        if (plugin.value(QLatin1String("Parent")).toString().isEmpty())
            continue;
        pluginIds.insert(plugin.pluginId());
    }

    for (const auto &pluginId : pluginIds) {
        if (m_appletConfigs.contains(pluginId))
            continue;
        
        // 移除原有的状态判断,确保所有子 Applet 都建立 DConfig 监听
        // 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());
        });
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants