Skip to content

fix: fix control center initialization and navigation issues#3049

Merged
deepin-bot[bot] merged 1 commit intolinuxdeepin:masterfrom
caixr23:bug-351937
Mar 7, 2026
Merged

fix: fix control center initialization and navigation issues#3049
deepin-bot[bot] merged 1 commit intolinuxdeepin:masterfrom
caixr23:bug-351937

Conversation

@caixr23
Copy link
Contributor

@caixr23 caixr23 commented Mar 5, 2026

  1. Initialize m_activeObject to nullptr instead of m_root to prevent invalid access
  2. Add null pointer checks in QML components to avoid crashes when activeObject is null
  3. Implement timer-based fallback mechanism to ensure UI displays even if plugins hang
  4. Fix navigation logic to handle cases where no active object is set

Log: Fixed control center startup issues and navigation stability

Influence:

  1. Test control center startup with various plugin loading scenarios
  2. Verify navigation works correctly when switching between modules
  3. Test back button functionality when no parent page exists
  4. Verify UI stability when plugins fail to load
  5. Test scrollbar functionality in second page view

fix: 修复控制中心初始化和导航问题

  1. 将 m_activeObject 初始化为 nullptr 而非 m_root,防止无效访问
  2. 在 QML 组件中添加空指针检查,避免 activeObject 为空时崩溃
  3. 实现基于定时器的回退机制,确保即使插件卡死也能显示界面
  4. 修复导航逻辑,处理没有设置活动对象的情况

Log: 修复控制中心启动问题和导航稳定性

Influence:

  1. 测试控制中心在各种插件加载场景下的启动情况
  2. 验证在模块间切换时导航功能正常工作
  3. 测试不存在父页面时的返回按钮功能
  4. 验证插件加载失败时的 UI 稳定性
  5. 测试第二页面视图中的滚动条功能

PMS: BUG-351937

Summary by Sourcery

Improve control center initialization robustness and navigation behavior when no active object is available.

Bug Fixes:

  • Initialize the control center's active object as null instead of the root to avoid invalid access during startup.
  • Ensure the main window shows the root page if no URL is pending and no active object is set when plugins finish loading.
  • Prevent crashes in QML by guarding navigation updates and back-button state against a null activeObject.

Enhancements:

  • Introduce a timer-based fallback to trigger UI display if plugin loading hangs, improving startup resilience.
  • Adjust the second-page view to ignore updates when there is no active object, avoiding unnecessary work.

@caixr23 caixr23 requested a review from mhduiy March 5, 2026 08:40
@sourcery-ai
Copy link

sourcery-ai bot commented Mar 5, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adjusts control center initialization to avoid invalid active object usage, adds defensive null checks in QML for navigation/back-button handling, and introduces a timer-based fallback to ensure the main UI shows even when plugins hang or fail to set an active object.

Sequence diagram for control center startup and timer-based fallback UI display

sequenceDiagram
    actor User
    participant DccManager
    participant PluginManager
    participant QTimer
    participant UI

    User->>DccManager: startApplication()
    activate DccManager
    DccManager->>DccManager: construct DccManager
    DccManager->>DccObject: new DccObject() as m_root
    DccManager->>DccManager: m_activeObject = nullptr
    DccManager->>PluginManager: connect addObject, loadAllFinished
    DccManager->>QTimer: new QTimer(this) as m_showTimer
    DccManager->>QTimer: start(5000)
    deactivate DccManager

    par Plugin_loading_completes
        PluginManager-->>DccManager: loadAllFinished()
        activate DccManager
        DccManager->>DccManager: tryShow()
        alt showUrl is empty and activeObject is null
            DccManager->>DccManager: clearShowParam()
            DccManager->>UI: showPage(m_root, empty)
        else showUrl is empty and activeObject not null
            DccManager->>DccManager: clearShowParam()
        else showUrl is not empty
            DccManager->>UI: showPage(activeObject, showUrl)
        end
        deactivate DccManager
    and Timer_fallback_triggers
        QTimer-->>DccManager: timeout()
        activate DccManager
        DccManager->>DccManager: tryShow()
        alt showUrl is empty and activeObject is null
            DccManager->>DccManager: clearShowParam()
            DccManager->>UI: showPage(m_root, empty)
        else other_conditions
            DccManager->>DccManager: handleAccordingToState()
        end
        deactivate DccManager
    end
Loading

Class diagram for updated DccManager initialization and fallback timer

classDiagram
    class DccApp
    class PluginManager
    class DccObject
    class QTimer

    class DccManager {
        - DccObject* m_root
        - DccObject* m_activeObject
        - DccObject* m_hideObjects
        - DccObject* m_noAddObjects
        - DccObject* m_noParentObjects
        - PluginManager* m_plugins
        - QTimer* m_showTimer
        - QString m_showUrl
        + DccManager(QObject* parent)
        + ~DccManager()
        + void clearShowParam()
        + void tryShow()
        + void showPage(DccObject* object, QString params)
    }

    DccManager --|> DccApp
    DccManager o--> DccObject : m_root
    DccManager o--> DccObject : m_activeObject
    DccManager o--> DccObject : m_hideObjects
    DccManager o--> DccObject : m_noAddObjects
    DccManager o--> DccObject : m_noParentObjects
    DccManager o--> PluginManager : m_plugins
    DccManager o--> QTimer : m_showTimer
Loading

File-Level Changes

Change Details Files
Initialize the control center without an active object and add a timer-based fallback to show the root page if plugins hang or never set an active object.
  • Change m_activeObject initial value from the root object to nullptr to prevent invalid early access.
  • Create and start a QTimer in the DccManager constructor to call tryShow() after a delay instead of immediately calling waitShowPage().
  • Update tryShow() to show the root page when no show URL is pending and no active object is set, then clear show parameters.
src/dde-control-center/dccmanager.cpp
Harden QML navigation and scrolling behavior against null activeObject and improve styling/readability.
  • Guard back button enablement with a check that DccApp.activeObject exists before accessing its parentName.
  • Update SecondPage right-view logic to early return when there is no activeObject, avoiding dereferencing null.
  • Reformat ScrollBar definition and minor JS handler spacing for consistency and readability, keeping behavior the same aside from null checks.
  • Adjust SPDX copyright year range in SecondPage.qml.
src/dde-control-center/plugin/DccWindow.qml
src/dde-control-center/plugin/SecondPage.qml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The m_showTimer currently runs indefinitely and keeps calling tryShow() every 5 seconds; consider making it single-shot or explicitly stopping it once the UI has been shown or an m_activeObject/m_showUrl is set to avoid unnecessary repeated work.
  • In tryShow(), when the fallback branch (m_showUrl.isEmpty() && !m_activeObject) calls showPage(m_root, QString()), it might be safer to also stop or disable the timer there to prevent repeated fallback invocations after the root page has already been displayed.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `m_showTimer` currently runs indefinitely and keeps calling `tryShow()` every 5 seconds; consider making it single-shot or explicitly stopping it once the UI has been shown or an `m_activeObject`/`m_showUrl` is set to avoid unnecessary repeated work.
- In `tryShow()`, when the fallback branch (`m_showUrl.isEmpty() && !m_activeObject`) calls `showPage(m_root, QString())`, it might be safer to also stop or disable the timer there to prevent repeated fallback invocations after the root page has already been displayed.

## Individual Comments

### Comment 1
<location path="src/dde-control-center/dccmanager.cpp" line_range="77-79" />
<code_context>
     connect(m_plugins, &PluginManager::addObject, this, &DccManager::addObject, Qt::QueuedConnection);
     connect(m_plugins, &PluginManager::loadAllFinished, this, &DccManager::tryShow, Qt::QueuedConnection);
-    waitShowPage("system", QDBusMessage());
+    m_showTimer = new QTimer(this);
+    connect(m_showTimer, &QTimer::timeout, this, &DccManager::tryShow);
+    m_showTimer->start(5000); // 防止插件卡死不显示界面
 }

</code_context>
<issue_to_address>
**suggestion (bug_risk):** Consider stopping the timer once the initial page is successfully shown to avoid repeated wakeups.

With the new QTimer, `tryShow()` now runs every 5 seconds for as long as the window exists. Once the initial page is shown (e.g., `m_activeObject` is set and `m_showUrl` is stable), consider stopping `m_showTimer` in `tryShow()` to avoid unnecessary work and to prevent future calls from affecting state if `m_showUrl` is later cleared.

Suggested implementation:

```cpp
void DccManager::tryShow()
{
    if(m_showUrl.isEmpty() && !m_activeObject){
        clearShowParam();
        showPage(m_root, QString());
        if (m_showTimer) {
            m_showTimer->stop();
        }
        return;
    }
    if (m_showUrl.isEmpty()) {

```

Depending on the rest of `tryShow()` (which is not fully visible here), you may also want to stop `m_showTimer` in other branches where the target page is successfully shown (e.g., after a successful `showPage(...)` based on a non-empty `m_showUrl`). The pattern would be the same: after confirming that the page has been shown as intended, call `if (m_showTimer) m_showTimer->stop();` to avoid further wakeups.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@deepin-bot
Copy link

deepin-bot bot commented Mar 5, 2026

TAG Bot

New tag: 6.1.75
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #3051

@robertkill
Copy link
Contributor

Code review points for PR #3049 (Translated to English due to tool limitations with Chinese input):

1. Default startup behavior change
The waitShowPage("system", QDBusMessage()) call was removed from the constructor. Previously, it defaulted to the "system" module. Now it falls back to m_root (home) after a 5-second timeout in tryShow(). Please confirm if this change in default behavior is intended.

2. m_showTimer lifecycle management
The m_showTimer is created and started in the constructor, but also created in waitShowPage() if it doesn't exist, and deleted in clearShowParam(). The management logic is scattered, which might make it hard to maintain. Suggest unifying the timer management.

3. 5-second timeout is quite long
Normal plugin loading usually takes milliseconds. A 5-second wait might affect user experience. Suggest reducing it to 2-3 seconds and adding a warning log (e.g., qCWarning) when timeout occurs.

4. Code style issue in tryShow()

if(m_showUrl.isEmpty() && !m_activeObject){

Missing spaces after if and before {. Suggested:

if (m_showUrl.isEmpty() && !m_activeObject) {

5. Completeness of m_activeObject null pointer protection
After changing m_activeObject default from m_root to nullptr, please ensure all access points have null checks. While QML side (DccWindow.qml, SecondPage.qml) seems covered, double-check if any C++ access points are missed.

@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: caixr23, robertkill

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

1. Initialize m_activeObject to nullptr instead of m_root to prevent
invalid access
2. Add null pointer checks in QML components to avoid crashes when
activeObject is null
3. Implement timer-based fallback mechanism to ensure UI displays even
if plugins hang
4. Fix navigation logic to handle cases where no active object is set

Log: Fixed control center startup issues and navigation stability

Influence:
1. Test control center startup with various plugin loading scenarios
2. Verify navigation works correctly when switching between modules
3. Test back button functionality when no parent page exists
4. Verify UI stability when plugins fail to load
5. Test scrollbar functionality in second page view

fix: 修复控制中心初始化和导航问题

1. 将 m_activeObject 初始化为 nullptr 而非 m_root,防止无效访问
2. 在 QML 组件中添加空指针检查,避免 activeObject 为空时崩溃
3. 实现基于定时器的回退机制,确保即使插件卡死也能显示界面
4. 修复导航逻辑,处理没有设置活动对象的情况

Log: 修复控制中心启动问题和导航稳定性

Influence:
1. 测试控制中心在各种插件加载场景下的启动情况
2. 验证在模块间切换时导航功能正常工作
3. 测试不存在父页面时的返回按钮功能
4. 验证插件加载失败时的 UI 稳定性
5. 测试第二页面视图中的滚动条功能

PMS: BUG-351937
@caixr23
Copy link
Contributor Author

caixr23 commented Mar 7, 2026

/forcemerge

@deepin-bot
Copy link

deepin-bot bot commented Mar 7, 2026

This pr force merged! (status: blocked)

@deepin-bot deepin-bot bot merged commit 0fe00ee into linuxdeepin:master Mar 7, 2026
16 of 19 checks passed
@deepin-ci-robot
Copy link

deepin pr auto review

这段代码主要对 DDE 控制中心的初始化流程、空指针安全性以及界面交互逻辑进行了修改。以下是详细的代码审查意见:

1. 代码逻辑与安全性分析

修改点 1:dccmanager.cppm_activeObject 初始化

// 修改前
, m_activeObject(m_root)
// 修改后
, m_activeObject(nullptr)
  • 审查意见:改进良好
  • 解释:将初始状态置为 nullptr 是更严谨的做法。在 tryShow() 的逻辑中,增加了对 m_activeObject 是否为空的判断。如果初始为 m_root,可能会导致在未加载任何页面时逻辑判断出现偏差。这有助于明确"当前无激活页面"的状态。

修改点 2:dccmanager.cpptryShow() 的逻辑增强

void DccManager::tryShow()
{
    if (m_showUrl.isEmpty() && !m_activeObject) {
        clearShowParam();
        showPage(m_root, QString());
        return;
    }
    // ...
}
  • 审查意见:改进良好
  • 解释:这段代码增加了一个兜底逻辑。当没有待显示的 URL 且当前没有激活对象时,默认显示根页面(主页)。这防止了界面在特定状态下"卡住"或什么都不显示的情况,增强了程序的健壮性。

修改点 3:dccmanager.cppm_showTimer 的初始化与启动

// 构造函数中
m_showTimer = new QTimer(this);
connect(m_showTimer, &QTimer::timeout, this, &DccManager::tryShow);
m_showTimer->start(5000); // 防止插件卡死不显示界面

// waitShowPage 中
// ...
if (!m_showTimer) {
    m_showTimer = new QTimer(this);
    connect(m_showTimer, &QTimer::timeout, this, &DccManager::tryShow);
}
m_showTimer->start(50); // 重新启动,覆盖之前的5秒
// ...
  • 审查意见:存在潜在风险与逻辑冲突
  • 解释
    1. 内存泄漏风险:在构造函数中创建了 m_showTimer 并启动了 5 秒定时器。而在 waitShowPage 中,如果 m_showTimer 为空(虽然构造函数已创建,这里实际上很难为空,除非被置空),又会 new 一个。如果 m_showTimer 是成员变量且未在析构函数中正确处理(虽然 QObject 亲子机制会自动析构,但逻辑上重复创建是不合理的),这里逻辑显得混乱。
    2. 定时器冲突:构造函数启动了一个 5000ms 的定时器,意图是"防止插件卡死"。但是,一旦调用 waitShowPage(通常在 DBus 消息到达时调用),会执行 m_showTimer->start(50)。这会取消之前的 5000ms 定时器并重置为 50ms。
    3. 建议:建议将 m_showTimer 的初始化统一放在构造函数中,且不要在构造函数中直接 start。或者,明确区分"初始化超时"和"页面显示超时"这两个不同的定时器用途。如果是为了防止 loadAllFinished 信号不触发,应该连接 loadAllFinished 信号来 stop 这个定时器,而不是简单地被 waitShowPage 覆盖。

修改点 4:DccWindow.qml 中的空指针检查

enabled: DccApp.activeObject && DccApp.activeObject.parentName.length !== 0 && ...
  • 审查意见:改进良好(必要)
  • 解释:增加了 DccApp.activeObject && 的判断。由于 C++ 端将 m_activeObject 初始值改为了 nullptr,QML 端必须增加空指针检查,否则在应用启动初期访问 parentName 会导致报错或界面异常。

修改点 5:SecondPage.qml 中的空指针检查

var activeObj = DccApp.activeObject
if (!activeObj || activeObj === dccObj) {
    return
}
  • 审查意见:改进良好(必要)
  • 解释:同上,增加了 !activeObj 判断,防止在 activeObject 为空时逻辑出错。

修改点 6:SecondPage.qml 中的代码格式化

onPressed: function (mouse) { ... }
ScrollBar.vertical: ScrollBar { width: 10 }
  • 审查意见:代码风格改进
  • 解释:将单行代码格式化为多行,符合 QML 规范,提高了可读性。

2. 代码性能与质量

  • 性能m_showTimer->start(50) 意味着 50ms 后尝试显示,这是一个非常短的延迟,通常用于确保 UI 布局就绪。性能影响可以忽略不计。
  • 质量:整体上,这次修改提高了代码的健壮性(Robustness),通过处理空指针情况,减少了崩溃或界面卡死的可能性。

3. 综合改进建议

针对 dccmanager.cpp 中定时器的逻辑,建议进行如下重构,以消除逻辑混乱:

建议修改方案:

  1. 在构造函数中只初始化定时器,不启动(或者仅在特定条件下启动保护性定时器)。
  2. 如果目的是"如果插件加载卡住,5秒后强制显示",应该监听加载完成信号来停止它。
  3. waitShowPage 中的逻辑应保持独立,用于处理即时显示请求。
// 构造函数中
m_showTimer = new QTimer(this);
m_showTimer->setSingleShot(true); // 建议设置为单次触发
connect(m_showTimer, &QTimer::timeout, this, &DccManager::tryShow);

// 如果确实需要一个启动时的保护定时器,建议这样处理:
// connect(m_plugins, &PluginManager::loadAllFinished, m_showTimer, &QTimer::stop);
// m_showTimer->start(5000); 

// waitShowPage 中
if (m_showTimer->isActive()) {
    m_showTimer->stop(); // 先停止之前的定时任务(无论是保护任务还是之前的show任务)
}
m_showTimer->start(50); // 启动新的短延时任务

总结:
除了 dccmanager.cpp 中关于定时器的管理逻辑略显混乱且存在潜在的逻辑覆盖问题外,其余修改(特别是空指针检查)是非常正确且必要的,能够有效提升程序的稳定性。建议重点优化定时器的启动与停止逻辑。

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