Skip to content

Conversation

@wjyrich
Copy link
Contributor

@wjyrich wjyrich commented Sep 28, 2025

  1. Add requestCloseAll method to handle close all action for grouped applications
  2. When close all is triggered, find all windows with the same desktop ID and close them
  3. Use X11Utils to send close commands to individual windows
  4. Add necessary includes for DockGroupModel and X11Utils
  5. Update action handler to call requestCloseAll instead of requestClose for close all action

feat: 实现关闭所有窗口功能

  1. 添加 requestCloseAll 方法来处理分组应用的关闭所有操作
  2. 当触发关闭所有时,查找具有相同桌面 ID 的所有窗口并关闭它们
  3. 使用 X11Utils 向各个窗口发送关闭命令
  4. 添加 DockGroupModel 和 X11Utils 的必要包含
  5. 更新操作处理器,为关闭所有操作调用 requestCloseAll 而不是 requestClose

Pms: BUG-334659

Summary by Sourcery

Implement close-all windows action for grouped applications

New Features:

  • Add requestCloseAll to close all windows of a grouped application by matching desktop IDs
  • Use X11Utils to send close commands for each window in requestCloseAll
  • Invoke requestCloseAll for the close-all action instead of requestClose

Enhancements:

  • Include DockGroupModel and X11Utils in DockGlobalElementModel

@sourcery-ai
Copy link

sourcery-ai bot commented Sep 28, 2025

Reviewer's Guide

Introduce a new requestCloseAll method in DockGlobalElementModel to batch-close grouped application windows by desktop ID using X11Utils, update the CLOSEALL action to use this method, and add necessary interface declarations and includes.

Sequence diagram for the new close all windows functionality

sequenceDiagram
  participant User as actor User
  participant DockGlobalElementModel
  participant DockCombineModel
  participant X11Utils
  User->>DockGlobalElementModel: Trigger CLOSEALL action
  DockGlobalElementModel->>DockCombineModel: Get windows with matching desktop ID
  DockCombineModel-->>DockGlobalElementModel: Return list of window IDs
  loop For each window ID
    DockGlobalElementModel->>X11Utils: closeWindow(winId)
  end
Loading

Updated class diagram for DockGlobalElementModel and related classes

classDiagram
  class DockGlobalElementModel {
    +void requestClose(const QModelIndex &index, bool force = false) const
    +void requestCloseAll(const QModelIndex &index) const
    +void requestUpdateWindowGeometry(const QModelIndex &index, const QRect &geometry, QObject *delegate = nullptr) const
  }
  class DockCombineModel {
    +int rowCount()
    +QModelIndex index(int row, int column)
  }
  class X11Utils {
    +static X11Utils* instance()
    +void closeWindow(uint32_t winId)
  }
  DockGlobalElementModel --> DockCombineModel : uses
  DockGlobalElementModel --> X11Utils : uses
Loading

File-Level Changes

Change Details Files
Implement requestCloseAll to batch-close grouped windows by desktop ID
  • Add requestCloseAll method in DockGlobalElementModel
  • Iterate through DockCombineModel entries to collect window IDs with matching DesktopIdRole
  • Invoke X11Utils::instance()->closeWindow for each collected window ID
panels/dock/taskmanager/dockglobalelementmodel.cpp
Update CLOSEALL action to invoke the batch-close method
  • Replace requestClose call with requestCloseAll in the DOCK_ACTION_CLOSEALL branch
panels/dock/taskmanager/dockglobalelementmodel.cpp
Declare and include support for the new batch-close functionality
  • Add requestCloseAll declaration to dockglobalelementmodel.h
  • Include DockGroupModel and X11Utils headers in implementation
panels/dock/taskmanager/dockglobalelementmodel.h
panels/dock/taskmanager/dockglobalelementmodel.cpp

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 there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `panels/dock/taskmanager/dockglobalelementmodel.cpp:419-420` </location>
<code_context>
+                QModelIndex idx = combineModel->index(i, 0);
+                QString desktopId = idx.data(TaskManager::DesktopIdRole).toString();
+                if (desktopId == targetDesktopId) {
+                    uint32_t winId = idx.data(TaskManager::WinIdRole).toUInt();
+                    if (winId != 0) {
+                       windowIds.append(winId);
+                    }
</code_context>

<issue_to_address>
**nitpick:** Zero winId is skipped without explanation.

Clarify in the code why winId == 0 is skipped, or add handling for cases where 0 may be valid.
</issue_to_address>

### Comment 2
<location> `panels/dock/taskmanager/dockglobalelementmodel.cpp:426-427` </location>
<code_context>
+            }
+                
+            for (uint32_t winId : windowIds) {
+                X11Utils::instance()->closeWindow(winId);
+            }
+        }
</code_context>

<issue_to_address>
**suggestion (bug_risk):** No error handling for failed window close operations.

Add error handling or logging to capture failures from X11Utils::closeWindow, so issues are not missed.

```suggestion
            for (uint32_t winId : windowIds) {
                if (!X11Utils::instance()->closeWindow(winId)) {
                    qWarning() << "Failed to close window with ID:" << winId;
                }
```
</issue_to_address>

### Comment 3
<location> `panels/dock/taskmanager/dockglobalelementmodel.cpp:404` </location>
<code_context>
     qWarning() << "unable to close app not running";
 }
+
+void DockGlobalElementModel::requestCloseAll(const QModelIndex &index) const
+{
+    auto data = m_data.value(index.row());
</code_context>

<issue_to_address>
**issue (complexity):** Consider moving the window-closing logic into a DockCombineModel helper to simplify requestCloseAll to a single method call.

You can collapse almost all of the nested loops and X11 calls into a single helper on `DockCombineModel`, then keep `requestCloseAll` down to one cast-and-dispatch. For example:

In DockCombineModel (new method):
```cpp
// dockgroupmodel.h / dockgroupmodel.cpp
void DockCombineModel::closeWindowsOnDesktop(const QString &desktopId) const
{
    QList<uint32_t> ids;
    for (int row = 0; row < rowCount(); ++row) {
        const auto idx = index(row, 0);
        if (idx.data(TaskManager::DesktopIdRole).toString() != desktopId)
            continue;
        uint32_t win = idx.data(TaskManager::WinIdRole).toUInt();
        if (win)
            ids << win;
    }
    for (auto win : ids)
        X11Utils::instance()->closeWindow(win);
}
```

Then in DockGlobalElementModel::requestCloseAll, replace all of the manual filtering with:
```cpp
void DockGlobalElementModel::requestCloseAll(const QModelIndex &index) const
{
    auto [id, sourceModel, sourceRow] = m_data.value(index.row());
    if (auto combine = qobject_cast<DockCombineModel*>(sourceModel)) {
        const QString desktopId = sourceModel->index(sourceRow, 0)
                                      .data(TaskManager::DesktopIdRole)
                                      .toString();
        combine->closeWindowsOnDesktop(desktopId);
    }
}
```

This keeps “close-all” logic in your model, removes deep nesting in DockGlobalElementModel, and preserves exactly the same behavior.
</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.

Comment on lines 419 to 420
uint32_t winId = idx.data(TaskManager::WinIdRole).toUInt();
if (winId != 0) {
Copy link

Choose a reason for hiding this comment

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

nitpick: Zero winId is skipped without explanation.

Clarify in the code why winId == 0 is skipped, or add handling for cases where 0 may be valid.

qWarning() << "unable to close app not running";
}

void DockGlobalElementModel::requestCloseAll(const QModelIndex &index) const
Copy link

Choose a reason for hiding this comment

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

issue (complexity): Consider moving the window-closing logic into a DockCombineModel helper to simplify requestCloseAll to a single method call.

You can collapse almost all of the nested loops and X11 calls into a single helper on DockCombineModel, then keep requestCloseAll down to one cast-and-dispatch. For example:

In DockCombineModel (new method):

// dockgroupmodel.h / dockgroupmodel.cpp
void DockCombineModel::closeWindowsOnDesktop(const QString &desktopId) const
{
    QList<uint32_t> ids;
    for (int row = 0; row < rowCount(); ++row) {
        const auto idx = index(row, 0);
        if (idx.data(TaskManager::DesktopIdRole).toString() != desktopId)
            continue;
        uint32_t win = idx.data(TaskManager::WinIdRole).toUInt();
        if (win)
            ids << win;
    }
    for (auto win : ids)
        X11Utils::instance()->closeWindow(win);
}

Then in DockGlobalElementModel::requestCloseAll, replace all of the manual filtering with:

void DockGlobalElementModel::requestCloseAll(const QModelIndex &index) const
{
    auto [id, sourceModel, sourceRow] = m_data.value(index.row());
    if (auto combine = qobject_cast<DockCombineModel*>(sourceModel)) {
        const QString desktopId = sourceModel->index(sourceRow, 0)
                                      .data(TaskManager::DesktopIdRole)
                                      .toString();
        combine->closeWindowsOnDesktop(desktopId);
    }
}

This keeps “close-all” logic in your model, removes deep nesting in DockGlobalElementModel, and preserves exactly the same behavior.

requestClose(index, true);
} else if (action == DOCK_ACTION_CLOSEALL) {
requestClose(index);
requestCloseAll(index);
Copy link
Member

Choose a reason for hiding this comment

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

是不是直接把 DockGlobalElementModel::requestNewInstance 的实现往上挪一层比较合适?在 DockGroupModel 提供 requestNewInstance 的实现,然后这里就可以去掉了。

cc @tsic404

18202781743
18202781743 previously approved these changes Sep 29, 2025
1. Add requestCloseAll method to handle close all action for grouped
applications
2. When close all is triggered, find all windows with the same desktop
ID and close them
3. Use X11Utils to send close commands to individual windows
4. Add necessary includes for DockGroupModel and X11Utils
5. Update action handler to call requestCloseAll instead of requestClose
for close all action

feat: 实现关闭所有窗口功能

1. 添加 requestCloseAll 方法来处理分组应用的关闭所有操作
2. 当触发关闭所有时,查找具有相同桌面 ID 的所有窗口并关闭它们
3. 使用 X11Utils 向各个窗口发送关闭命令
4. 添加 DockGroupModel 和 X11Utils 的必要包含
5. 更新操作处理器,为关闭所有操作调用 requestCloseAll 而不是
requestClose

Pms: BUG-334659
@deepin-ci-robot
Copy link

deepin pr auto review

我来分析这段代码的变更和改进:

1. 代码结构变更

  • DockGlobalElementModel 中移除了 requestNewInstance 方法的实现
  • DockGroupModel 中添加了 requestNewInstance 方法的实现
  • DockGlobalElementModel 中修改了 requestActivate 方法,增加了类型检查

2. 改进点分析

优点:

  1. 更好的职责分离:将 requestNewInstance 的实现移到了 DockGroupModel 中,使得每个模型类更加专注和内聚。
  2. 类型安全增强:在 DockGlobalElementModel::requestActivate 中增加了 dynamic_cast 类型检查,避免了潜在的类型不安全问题。
  3. 代码复用requestNewInstance 的实现被移到了更合适的类中,避免了重复代码。

需要改进的地方:

  1. 错误处理不足

    • requestNewInstance 中,QProcess 的启动和执行没有错误处理机制。如果 dde-am 命令执行失败,用户不会得到任何反馈。
    • 建议:添加错误处理,例如检查 process.exitCode()process.errorString()
  2. 性能问题

    • process.waitForFinished() 会阻塞当前线程,可能导致界面卡顿。
    • 建议:使用异步方式处理进程,或者将耗时操作放到单独的线程中。
  3. 代码安全性

    • 直接使用外部命令 "dde-am" 存在潜在的安全风险,特别是当 action 参数来自用户输入时。
    • 建议:对 action 参数进行验证,只允许预定义的安全操作。
  4. 代码可维护性

    • requestNewInstance 方法中的字符串常量(如 "desktop/%1")应该定义为类常量,便于统一修改。
    • 建议:将这些字符串定义为常量或枚举。
  5. 代码复用

    • requestNewInstanceDockGroupModel 和原来的 DockGlobalElementModel 中有相似的实现,可以考虑提取公共部分。

3. 具体改进建议

// 1. 添加错误处理
void DockGroupModel::requestNewInstance(const QModelIndex &index, const QString &action) const {
    if (action == DOCK_ACTION_DOCK) {
        auto desktopId = index.data(TaskManager::DesktopIdRole).toString();
        TaskManagerSettings::instance()->toggleDockedElement(QStringLiteral("desktop/%1").arg(desktopId));
    } else if (action == DOCK_ACTION_FORCEQUIT) {
        requestClose(index, true);
    } else if (action == DOCK_ACTION_CLOSEALL) {
        requestClose(index);
    } else {
        auto desktopId = index.data(TaskManager::DesktopIdRole).toString();
        QProcess process;
        process.setProcessChannelMode(QProcess::MergedChannels);
        
        // 验证 action 参数
        static const QStringList allowedActions = {"action1", "action2", "action3"}; // 替换为实际允许的操作
        if (!allowedActions.contains(action)) {
            qWarning() << "Invalid action requested:" << action;
            return;
        }
        
        process.start("dde-am", {"--by-user", desktopId, action});
        if (!process.waitForFinished(5000)) { // 设置超时
            qWarning() << "Process execution timed out or failed:" << process.errorString();
            return;
        }
        
        if (process.exitCode() != 0) {
            qWarning() << "Process exited with error:" << process.readAllStandardError();
        }
    }
}

// 2. 使用异步处理
// 可以考虑使用 QFuture 或信号槽机制来处理进程执行

// 3. 定义常量
class DockGroupModel : public RoleGroupModel, public AbstractTaskManagerInterface {
private:
    static constexpr const char* DESKTOP_ID_PREFIX = "desktop/%1";
    // ...
};

// 4. 提取公共方法
void DockGroupModel::handleProcessAction(const QString &desktopId, const QString &action) const {
    QProcess process;
    process.setProcessChannelMode(QProcess::MergedChannels);
    process.start("dde-am", {"--by-user", desktopId, action});
    // ... 错误处理
}

4. 总结

这次代码重构在职责分离和类型安全方面做出了改进,但在错误处理、性能优化和安全性方面还有提升空间。建议按照上述建议进行改进,以提高代码的健壮性、安全性和可维护性。

@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 18202781743, 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

@wjyrich wjyrich merged commit 413ba10 into linuxdeepin:master Sep 29, 2025
10 of 11 checks passed
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.

4 participants