Skip to content

fix: Fix the issue of tray area icons moving when released#1488

Merged
deepin-bot[bot] merged 1 commit intolinuxdeepin:masterfrom
pengfeixx:fix-288727
Mar 9, 2026
Merged

fix: Fix the issue of tray area icons moving when released#1488
deepin-bot[bot] merged 1 commit intolinuxdeepin:masterfrom
pengfeixx:fix-288727

Conversation

@pengfeixx
Copy link
Contributor

@pengfeixx pengfeixx commented Mar 9, 2026

Fix the issue of tray area icons moving when released

Log: Fix the issue of tray area icons moving when released
pms: BUG-288727

Summary by Sourcery

Fix tray icon reordering behaviour when dropping on the quick settings toggle and committing staged drops so icons no longer jump or move unexpectedly after release.

Bug Fixes:

  • Allow dropping tray items onto the quick settings toggle by moving them into the pinned section instead of rejecting the drop.
  • Ensure staged tray drops are committed using saved state and refreshed visual indexes to prevent icons from shifting unexpectedly after release.

@sourcery-ai
Copy link

sourcery-ai bot commented Mar 9, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Implements correct handling of tray icon drops onto the quick-settings action target and adjusts staged-drop commit logic to prevent tray icons from jumping/moving unexpectedly after drag-and-drop operations.

Class diagram for updated TraySortOrderModel drop handling

classDiagram
    class TraySortOrderModel {
        - QList~QString~ m_pinnedIds
        - QString m_stagedSurfaceId
        - int m_stagedVisualIndex
        - QSet~int~ forbiddenSections

        + bool dropToDockTray(QString draggedSurfaceId, int dropVisualIndex, bool isBefore)
        + void commitStagedDrop()
        + void clearStagedDrop()
        + void updateVisualIndexes()
        + void stagedDropChanged()
    }

    class SectionContainer {
        <<interface>> SectionContainer
        + bool removeOne(QString surfaceId)
    }

    TraySortOrderModel o--> SectionContainer : sourceSection

    class PinnedSection {
        + QList~QString~ ids
        + void move(int fromIndex, int toIndex)
        + int indexOf(QString surfaceId)
        + int count()
        + void append(QString surfaceId)
    }

    TraySortOrderModel *-- PinnedSection : m_pinnedIds

    class QuickSettingsTarget {
        + QString surfaceId
    }

    TraySortOrderModel .. QuickSettingsTarget : dropOnSurfaceId == internal/action-toggle-quick-settings

    class ForbiddenSections {
        + bool contains(int section)
    }

    TraySortOrderModel *-- ForbiddenSections : forbiddenSections

    class SectionsEnum {
        <<enumeration>>
        SECTION_PINNED
    }

    ForbiddenSections .. SectionsEnum : uses SECTION_PINNED

    note for TraySortOrderModel "dropToDockTray now moves or appends draggedSurfaceId into m_pinnedIds when dropped on QuickSettingsTarget, unless SECTION_PINNED is forbidden; commitStagedDrop now clears staged state before calling updateVisualIndexes and dropToDockTray"
Loading

File-Level Changes

Change Details Files
Handle drops onto the quick-settings toggle action by pinning the dragged tray item instead of rejecting the drop.
  • Previously drops onto the quick-settings action target returned false and performed no changes; now these drops are accepted unless the pinned section is forbidden.
  • When the dragged item is already in the pinned section, its position is updated to the end of the pinned list.
  • When the dragged item originates from another section, it is removed from the source section and appended to the pinned section, effectively pinning it via drop.
panels/dock/tray/traysortordermodel.cpp
Adjust staged drop commit sequence to avoid state inconsistencies and unintended icon movement.
  • Cache staged surfaceId and visualIndex before clearing staged state to avoid using cleared members when applying the drop.
  • Clear staged drop state and emit stagedDropChanged() before performing index updates and applying the drop.
  • Call updateVisualIndexes() explicitly prior to invoking dropToDockTray with the cached staged values, rather than relying on dropToDockTray for both responsibilities.
panels/dock/tray/traysortordermodel.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 - I've found 1 issue, and left some high level feedback:

  • In commitStagedDrop, you now call updateVisualIndexes() before dropToDockTray(surfaceId, visualIndex, true); please double-check that the visualIndex passed in is still valid after the reindexing and that dropToDockTray’s internal assumptions about indexes remain consistent with this new ordering.
  • In the new quick-settings branch of dropToDockTray, consider defensively handling the case where m_pinnedIds.indexOf(draggedSurfaceId) returns -1 before calling move(), to avoid undefined behavior if the internal state ever becomes inconsistent.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In commitStagedDrop, you now call updateVisualIndexes() before dropToDockTray(surfaceId, visualIndex, true); please double-check that the visualIndex passed in is still valid after the reindexing and that dropToDockTray’s internal assumptions about indexes remain consistent with this new ordering.
- In the new quick-settings branch of dropToDockTray, consider defensively handling the case where m_pinnedIds.indexOf(draggedSurfaceId) returns -1 before calling move(), to avoid undefined behavior if the internal state ever becomes inconsistent.

## Individual Comments

### Comment 1
<location path="panels/dock/tray/traysortordermodel.cpp" line_range="174-175" />
<code_context>
     if (dropOnSurfaceId == QLatin1String("internal/action-toggle-quick-settings")) {
-        return false;
+        if (forbiddenSections.contains(SECTION_PINNED)) return false;
+        if (sourceSection == &m_pinnedIds) {
+            m_pinnedIds.move(m_pinnedIds.indexOf(draggedSurfaceId), m_pinnedIds.count() - 1);
+        } else {
+            sourceSection->removeOne(draggedSurfaceId);
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard against a missing draggedSurfaceId in the pinned list before calling move().

This relies on `draggedSurfaceId` always being in `m_pinnedIds` when `sourceSection == &m_pinnedIds`. If that ever isn’t true, `indexOf` returns `-1` and `move(-1, …)` may assert or behave unpredictably. Please either guard with a `contains`/`indexOf` check before calling `move`, or add an assertion to document and enforce the invariant.
</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.

Fix the issue of tray area icons moving when released

Log: Fix the issue of tray area icons moving when released
pms: BUG-288727
@deepin-ci-robot
Copy link

deepin pr auto review

这段代码修改涉及到了托盘图标排序逻辑的调整,主要针对拖拽到"快速设置"区域的行为进行了重新定义,并优化了暂存拖拽操作的提交逻辑。以下是对代码的详细审查和改进建议:

1. 语法逻辑审查

修改点 1:dropToDockTray 函数中对 internal/action-toggle-quick-settings 的处理

if (dropOnSurfaceId == QLatin1String("internal/action-toggle-quick-settings")) {
    if (forbiddenSections.contains(SECTION_PINNED)) return false;
    if (sourceSection == &m_pinnedIds) {
        int idx = m_pinnedIds.indexOf(draggedSurfaceId);
        if (idx >= 0) {
            m_pinnedIds.move(idx, m_pinnedIds.count() - 1);
        }
    } else {
        sourceSection->removeOne(draggedSurfaceId);
        m_pinnedIds.append(draggedSurfaceId);
    }
    return true;
}

问题分析

  • 逻辑上,这段代码实现了将拖拽的图标移动到固定区域的末尾。
  • sourceSection 是一个指针,但没有进行空指针检查,如果 sourceSectionnullptr,调用 removeOne 会导致崩溃。
  • m_pinnedIds.movesourceSection->removeOne 都会触发模型更新,但没有显式调用 beginMoveRowsendMoveRows(假设这是一个 QAbstractItemModel 的子类),这可能导致视图不同步。

改进建议

if (dropOnSurfaceId == QLatin1String("internal/action-toggle-quick-settings")) {
    if (forbiddenSections.contains(SECTION_PINNED)) return false;
    
    if (!sourceSection) {
        qWarning() << "sourceSection is null in dropToDockTray";
        return false;
    }
    
    if (sourceSection == &m_pinnedIds) {
        int idx = m_pinnedIds.indexOf(draggedSurfaceId);
        if (idx >= 0) {
            // 如果已经在固定区域,只需移动到末尾
            beginMoveRows(QModelIndex(), idx, idx, QModelIndex(), m_pinnedIds.count());
            m_pinnedIds.move(idx, m_pinnedIds.count() - 1);
            endMoveRows();
        }
    } else {
        // 从其他区域移动到固定区域
        int sourceIdx = sourceSection->indexOf(draggedSurfaceId);
        if (sourceIdx >= 0) {
            beginRemoveRows(QModelIndex(), sourceIdx, sourceIdx);
            sourceSection->removeOne(draggedSurfaceId);
            endRemoveRows();
            
            beginInsertRows(QModelIndex(), m_pinnedIds.count(), m_pinnedIds.count());
            m_pinnedIds.append(draggedSurfaceId);
            endInsertRows();
        }
    }
    return true;
}

修改点 2:commitStagedDrop 函数的执行顺序调整

void TraySortOrderModel::commitStagedDrop() {
    if (m_stagedSurfaceId.isEmpty() || m_stagedVisualIndex < 0) {
        return;
    }
    
    // Save staged values before clearing
    QString surfaceId = m_stagedSurfaceId;
    int visualIndex = m_stagedVisualIndex;
    
    m_stagedSurfaceId.clear();
    m_stagedVisualIndex = -1;
    emit stagedDropChanged();
    
    updateVisualIndexes();
    dropToDockTray(surfaceId, visualIndex, true);
}

问题分析

  • 原代码先调用 dropToDockTray 再清除暂存状态,修改后先清除状态再调用 dropToDockTray
  • 这种修改可能导致 dropToDockTray 内部依赖 m_stagedSurfaceIdm_stagedVisualIndex 的逻辑出现问题。
  • updateVisualIndexes() 被移到了 dropToDockTray 之前,这可能导致 dropToDockTray 内部使用的索引不准确。

改进建议

void TraySortOrderModel::commitStagedDrop() {
    if (m_stagedSurfaceId.isEmpty() || m_stagedVisualIndex < 0) {
        return;
    }
    
    // Save staged values before clearing
    QString surfaceId = m_stagedSurfaceId;
    int visualIndex = m_stagedVisualIndex;
    
    // First perform the drop operation
    dropToDockTray(surfaceId, visualIndex, true);
    
    // Then clear staged state
    m_stagedSurfaceId.clear();
    m_stagedVisualIndex = -1;
    emit stagedDropChanged();
    
    // Finally update visual indexes after the drop operation
    updateVisualIndexes();
}

2. 代码质量审查

  1. 命名规范

    • m_pinnedIdsm_stagedSurfaceId 等变量命名符合 Qt 的命名规范(m_前缀表示成员变量)。
    • 建议将 SECTION_PINNED 改为更明确的枚举类型,如 enum SectionType { SectionPinned, ... }
  2. 代码可读性

    • dropToDockTray 函数较长,建议将不同逻辑分支提取为独立函数,如 handleDropToQuickSettingshandleDropToSameSurface 等。
    • 添加更多注释说明复杂逻辑,特别是关于 sourceSectionm_pinnedIds 的操作。
  3. 错误处理

    • 缺少对 sourceSection 的空指针检查。
    • 缺少对 draggedSurfaceId 是否在 sourceSection 中的检查。

3. 代码性能审查

  1. indexOfremoveOne 的性能

    • m_pinnedIds.indexOf(draggedSurfaceId) 是线性搜索,如果 m_pinnedIds 很大,性能会受影响。
    • 建议使用 QHash<QString, int> 维护一个索引映射,提高查找速度。
  2. 模型更新性能

    • 多次调用 beginMoveRows/endMoveRowsbeginRemoveRows/endInsertRows 可能导致多次视图更新。
    • 如果可能,考虑批量操作或使用 layoutChanged 信号。

4. 代码安全审查

  1. 空指针检查

    • sourceSection 没有进行空指针检查,可能导致崩溃。
    • 建议在函数开头添加 if (!sourceSection) return false;
  2. 边界检查

    • m_pinnedIds.move(idx, m_pinnedIds.count() - 1) 没有检查 idx 是否在有效范围内。
    • 建议添加 if (idx < 0 || idx >= m_pinnedIds.count()) return false;
  3. 数据一致性

    • 直接修改 m_pinnedIdssourceSection 可能导致数据不一致。
    • 建议使用事务机制或确保所有操作在失败时回滚。

5. 综合改进建议

以下是改进后的完整代码:

bool TraySortOrderModel::dropToDockTray(const QString &draggedSurfaceId, int dropVisualIndex, bool isBefore) {
    if (draggedSurfaceId.isEmpty()) {
        return false;
    }
    
    // Find source section
    QStringList *sourceSection = findSourceSection(draggedSurfaceId);
    if (!sourceSection) {
        qWarning() << "Source section not found for" << draggedSurfaceId;
        return false;
    }
    
    // Handle drop to quick settings
    if (dropOnSurfaceId == QLatin1String("internal/action-toggle-quick-settings")) {
        if (forbiddenSections.contains(SECTION_PINNED)) {
            return false;
        }
        
        return handleDropToPinnedSection(sourceSection, draggedSurfaceId);
    }
    
    // Handle other drop cases...
}

bool TraySortOrderModel::handleDropToPinnedSection(QStringList *sourceSection, const QString &surfaceId) {
    if (sourceSection == &m_pinnedIds) {
        int idx = m_pinnedIds.indexOf(surfaceId);
        if (idx >= 0 && idx < m_pinnedIds.count() - 1) {
            beginMoveRows(QModelIndex(), idx, idx, QModelIndex(), m_pinnedIds.count());
            m_pinnedIds.move(idx, m_pinnedIds.count() - 1);
            endMoveRows();
        }
    } else {
        int sourceIdx = sourceSection->indexOf(surfaceId);
        if (sourceIdx >= 0) {
            beginRemoveRows(QModelIndex(), sourceIdx, sourceIdx);
            sourceSection->removeOne(surfaceId);
            endRemoveRows();
            
            beginInsertRows(QModelIndex(), m_pinnedIds.count(), m_pinnedIds.count());
            m_pinnedIds.append(surfaceId);
            endInsertRows();
        }
    }
    return true;
}

void TraySortOrderModel::commitStagedDrop() {
    if (m_stagedSurfaceId.isEmpty() || m_stagedVisualIndex < 0) {
        return;
    }
    
    QString surfaceId = m_stagedSurfaceId;
    int visualIndex = m_stagedVisualIndex;
    
    // Perform the drop operation first
    dropToDockTray(surfaceId, visualIndex, true);
    
    // Then clear staged state
    m_stagedSurfaceId.clear();
    m_stagedVisualIndex = -1;
    emit stagedDropChanged();
    
    // Finally update visual indexes
    updateVisualIndexes();
}

总结

  1. 主要问题

    • 缺少空指针检查和边界检查。
    • 模型更新操作不完整,可能导致视图不同步。
    • commitStagedDrop 的执行顺序调整可能导致逻辑错误。
  2. 改进方向

    • 添加必要的错误检查和边界检查。
    • 确保模型更新操作完整(使用 beginMoveRows/endMoveRows 等)。
    • 优化 commitStagedDrop 的执行顺序。
    • 提高代码可读性,提取复杂逻辑为独立函数。
    • 优化性能,考虑使用哈希表提高查找速度。
  3. 安全建议

    • 添加日志记录关键操作,便于调试。
    • 考虑使用事务机制确保数据一致性。
    • 对外部输入(如 draggedSurfaceId)进行验证。

@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: BLumia, pengfeixx

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

@pengfeixx
Copy link
Contributor Author

/forcemerge

@deepin-bot
Copy link

deepin-bot bot commented Mar 9, 2026

This pr force merged! (status: unstable)

@deepin-bot deepin-bot bot merged commit dfb9a0a into linuxdeepin:master Mar 9, 2026
11 of 12 checks passed
@pengfeixx pengfeixx deleted the fix-288727 branch March 9, 2026 05:46
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