Skip to content

fix: fix touchscreen drag interruption issue#719

Merged
deepin-bot[bot] merged 1 commit intolinuxdeepin:masterfrom
18202781743:master
Mar 5, 2026
Merged

fix: fix touchscreen drag interruption issue#719
deepin-bot[bot] merged 1 commit intolinuxdeepin:masterfrom
18202781743:master

Conversation

@18202781743
Copy link
Contributor

@18202781743 18202781743 commented Mar 5, 2026

  1. Replaced DragHandler with MouseArea for drag handling to better
    support touchscreen interactions
  2. Changed visibility control from visible to opacity to prevent
    layout reflow during drag operations
  3. Updated drag activation logic to use MouseArea's drag state instead
    of DragHandler's active state
  4. Simplified drag initialization by directly setting Drag.imageSource
    on the root item
  5. Fixed drag state synchronization by binding Drag.active to
    mouseArea.drag.active

Log: Fixed touchscreen drag and drop interruption issues

Influence:

  1. Test touchscreen drag operations on icon items
  2. Verify drag visual feedback (opacity changes) during drag operations
  3. Test drag and drop functionality with mouse input
  4. Verify drag image appears correctly during drag operations
  5. Test drag cancellation and completion scenarios
  6. Verify no layout shifts occur during drag operations

fix: 修复触摸屏拖拽被打断的问题

  1. 将 DragHandler 替换为 MouseArea 以更好地支持触摸屏交互
  2. 将可见性控制从 visible 改为 opacity,防止拖拽过程中的布局重排
  3. 更新拖拽激活逻辑,使用 MouseArea 的拖拽状态而非 DragHandler 的活动
    状态
  4. 简化拖拽初始化,直接在根项目上设置 Drag.imageSource
  5. 通过将 Drag.active 绑定到 mouseArea.drag.active 来修复拖拽状态同步
    问题

Log: 修复触摸屏拖拽被打断的问题

Influence:

  1. 测试触摸屏上的图标拖拽操作
  2. 验证拖拽过程中的视觉反馈(透明度变化)
  3. 测试鼠标输入的拖放功能
  4. 验证拖拽过程中拖拽图像正确显示
  5. 测试拖拽取消和完成场景
  6. 验证拖拽过程中没有布局偏移发生

PMS: BUG-346077

Summary by Sourcery

Improve drag-and-drop behavior for icon items to avoid interruptions on touchscreens and prevent layout shifts during drags.

Bug Fixes:

  • Resolve touchscreen drag interruption by switching from DragHandler to a MouseArea-based drag implementation and synchronizing Drag.active with the MouseArea drag state.
  • Prevent layout reflow during drag operations by controlling icon visibility via opacity instead of the visible property.

@sourcery-ai
Copy link

sourcery-ai bot commented Mar 5, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Replaces DragHandler-based drag logic with a MouseArea-driven drag implementation for IconItemDelegate to better support touch screens, adjusts visibility handling to use opacity, and synchronizes Drag state with MouseArea.drag to avoid interruptions and layout shifts during drag operations.

Sequence diagram for the new touchscreen drag handling with MouseArea

sequenceDiagram
    actor User
    participant TouchDevice
    participant root as IconItemDelegate_root
    participant mouseArea as IconItemDelegate_mouseArea
    participant Drag as DragAttached

    User->>TouchDevice: Touch on icon
    TouchDevice->>mouseArea: press(Qt.LeftButton)
    mouseArea->>mouseArea: onPressed(mouse)
    mouseArea->>root: check dndEnabled
    alt drag_allowed
        mouseArea->>root: appIcon.grabToImage(callback)
        root-->>mouseArea: image result
        mouseArea->>Drag: set root.Drag.imageSource
    end

    User->>TouchDevice: Move finger
    TouchDevice->>mouseArea: drag events
    mouseArea->>mouseArea: drag.target = root
    mouseArea->>mouseArea: drag.active = true
    mouseArea->>Drag: Drag.active bound to mouseArea.drag.active
    Drag->>root: Drag.active true
    root->>root: opacity = 0
    root->>root: state dragged when mouseArea.drag.active

    User->>TouchDevice: Release finger
    TouchDevice->>mouseArea: release event
    mouseArea->>mouseArea: drag.active = false
    mouseArea->>Drag: Drag.active false (via binding)
    Drag->>root: Drag.active false
    root->>root: opacity = 1
    root->>root: exit dragged state
Loading

Updated class diagram for IconItemDelegate drag handling

classDiagram

    class IconItemDelegate_root {
        <<Control>>
        opacity : real
        property string text
        signal menuTriggered()
        Drag_dragType : DragType
        Drag_active : bool
        Drag_imageSource : url
    }

    class IconItemDelegate_mouseArea {
        <<MouseArea>>
        anchors_fill_parent : bool
        hoverEnabled : bool
        acceptedButtons : QtMouseButtons
        enabled : bool
        drag_target : IconItemDelegate_root
        drag_active : bool
        onPressed(mouse)
    }

    class DragAttached {
        <<DragAttached>>
        dragType : DragType
        active : bool
        imageSource : url
    }

    class DragState_dragged {
        <<State>>
        when : bool  ~mouseArea.drag.active~
        PropertyChanges_x : real
        PropertyChanges_y : real
    }

    IconItemDelegate_root o-- IconItemDelegate_mouseArea : contains
    IconItemDelegate_root o-- DragAttached : has_Drag
    IconItemDelegate_root o-- DragState_dragged : has_state

    IconItemDelegate_mouseArea --> IconItemDelegate_root : drag_target
    IconItemDelegate_mouseArea --> DragAttached : updates_imageSource
    DragAttached --> IconItemDelegate_mouseArea : active_bound_to_drag_active
Loading

File-Level Changes

Change Details Files
Switch drag interaction from DragHandler to MouseArea to support touch input and use MouseArea.drag state for all drag-related logic.
  • Remove DragHandler as the drag input handler on the icon control.
  • Introduce a full-area MouseArea with left-button handling and drag.target bound to the root item.
  • Use mouseArea.drag.active as the single source of truth for drag state, including the 'dragged' state condition.
qml/windowed/IconItemDelegate.qml
Adjust visual and Drag attached properties to avoid layout reflow and keep drag state in sync with MouseArea.
  • Replace root.visible toggling with opacity changes based on Drag.active to prevent layout shifts during drag.
  • Bind Drag.active on the root to mouseArea.drag.active to synchronize drag state.
  • Simplify Drag image initialization by setting root.Drag.imageSource from the grabbed icon image instead of configuring a separate dndItem.
qml/windowed/IconItemDelegate.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:

  • Switching from visible to opacity means the item still participates in layout and hit‑testing while invisible; if you intend the icon to be non-interactive during drag, consider also updating enabled or visible (or opacity + visible in a transition) to avoid unexpected input behavior.
  • The grabToImage call is now triggered on every left press instead of only after the drag becomes active, which may be unnecessarily expensive on quick taps; consider deferring grabToImage until the drag actually starts (e.g., in response to mouseArea.drag.active or movement beyond a threshold).
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Switching from `visible` to `opacity` means the item still participates in layout and hit‑testing while invisible; if you intend the icon to be non-interactive during drag, consider also updating `enabled` or `visible` (or `opacity` + `visible` in a transition) to avoid unexpected input behavior.
- The `grabToImage` call is now triggered on every left press instead of only after the drag becomes active, which may be unnecessarily expensive on quick taps; consider deferring `grabToImage` until the drag actually starts (e.g., in response to `mouseArea.drag.active` or movement beyond a threshold).

## Individual Comments

### Comment 1
<location path="qml/windowed/IconItemDelegate.qml" line_range="15" />
<code_context>
 Control {
     id: root
-    visible: !dragHandler.active
+    opacity: Drag.active ? 0 : 1

     property string text: display.startsWith("internal/category/") ? getCategoryName(display.substring(18)) : display
</code_context>
<issue_to_address>
**issue (bug_risk):** Using opacity instead of visible may leave the item interactive while visually hidden.

With `visible: !dragHandler.active`, the control was removed from hit-testing while dragging. Using `opacity: Drag.active ? 0 : 1` keeps it fully interactive even when invisible, so clicks/drag/hover can still be processed on a hidden item. If the goal is to hide it functionally during drag, consider restoring the `visible` condition or also disabling input (e.g., via `enabled` or combining `visible` with `opacity`).
</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.

BLumia
BLumia previously approved these changes Mar 5, 2026
1. Replaced DragHandler with MouseArea for drag handling to better
support touchscreen interactions
2. Changed visibility control from `visible` to `opacity` to prevent
layout reflow during drag operations
3. Updated drag activation logic to use MouseArea's drag state instead
of DragHandler's active state
4. Simplified drag initialization by directly setting Drag.imageSource
on the root item
5. Fixed drag state synchronization by binding Drag.active to
mouseArea.drag.active

Log: Fixed touchscreen drag and drop interruption issues

Influence:
1. Test touchscreen drag operations on icon items
2. Verify drag visual feedback (opacity changes) during drag operations
3. Test drag and drop functionality with mouse input
4. Verify drag image appears correctly during drag operations
5. Test drag cancellation and completion scenarios
6. Verify no layout shifts occur during drag operations

fix: 修复触摸屏拖拽被打断的问题

1. 将 DragHandler 替换为 MouseArea 以更好地支持触摸屏交互
2. 将可见性控制从 `visible` 改为 `opacity`,防止拖拽过程中的布局重排
3. 更新拖拽激活逻辑,使用 MouseArea 的拖拽状态而非 DragHandler 的活动
状态
4. 简化拖拽初始化,直接在根项目上设置 Drag.imageSource
5. 通过将 Drag.active 绑定到 mouseArea.drag.active 来修复拖拽状态同步
问题

Log: 修复触摸屏拖拽被打断的问题

Influence:
1. 测试触摸屏上的图标拖拽操作
2. 验证拖拽过程中的视觉反馈(透明度变化)
3. 测试鼠标输入的拖放功能
4. 验证拖拽过程中拖拽图像正确显示
5. 测试拖拽取消和完成场景
6. 验证拖拽过程中没有布局偏移发生

PMS: BUG-346077
@deepin-ci-robot
Copy link

deepin pr auto review

#,我对这段 QML 代码的变更进行了审查。以下是从语法逻辑、代码质量、代码性能和代码安全四个方面的详细分析和改进建议:

1. 语法逻辑

  • 变更点 1:版权年份更新
    • 分析SPDX-FileCopyrightText: 2024 - 2026
    • 评价:符合规范,逻辑正确。
  • 变更点 2:拖拽处理机制重构 (DragHandler -> MouseArea)
    • 分析:代码从使用 DragHandler 改为使用 MouseArea 来处理拖拽。
    • 逻辑问题
      • 拖拽启动逻辑:原代码在 DragHandler 激活时(开始拖动)通过 dndItem 启动拖拽。新代码在 MouseAreaonPressed 事件中(鼠标按下时)就立即抓取图标并设置 imageSource
      • 潜在 BugMouseAreaonPressed 在鼠标按下瞬间触发,此时用户可能只是点击而非拖拽。原代码有 dragThreshold: 1(虽然很低),意在区分点击和拖拽。新代码去掉了阈值判断,导致每次点击都会执行 grabToImage,这不符合常规交互逻辑(点击应该是选中或打开,而不是准备拖拽)。
      • 状态同步:新代码依赖 mouseArea.drag.active 来控制 Drag.activeroot.opacityMouseAreadrag 属性主要用于拖动 Item 本身(改变 x/y),而 Drag 附加属性用于启动系统级的 DnD(拖放)操作。两者混用需要非常小心。
    • 改进建议:应该在 onPressed 中仅做准备工作,真正启动 Drag(如调用 Drag.start() 或设置 active)应该在鼠标移动一定距离后(即 onPositionChanged 中判断位移)或者在 onReleased 之前的某个时刻。目前的实现逻辑上混淆了"按下"和"开始拖拽"。

2. 代码质量

  • 代码风格:QML 代码缩进和结构基本清晰。
  • 注释移除:原代码中有一段关于 dndItem 的详细 FIXME 注释,解释了为什么使用 dndItem 来处理拖拽(防止 Item 销毁时崩溃)。新代码移除了这段注释,并且移除了 dndItem 的相关逻辑。
    • 风险:这是一个高风险变更。原代码明确指出,直接使用当前 Item 进行拖拽会导致在特定场景(如翻页、关闭文件夹)下 Item 被销毁时发生崩溃。新代码直接在 root 上操作 root.Drag,如果 root 在拖拽过程中被销毁(例如快速切换视图),极大概率会复现之前的崩溃问题。
    • 改进建议:必须确认 root 的生命周期是否在拖拽期间得到了保护,或者重新引入一个稳定的 dndItem 代理对象来处理拖拽事件。如果确实解决了销毁问题,应添加注释说明解决方案。

3. 代码性能

  • 图像抓取频率
    • 现状grabToImage 被移到了 onPressed 中。
    • 问题:如前所述,只要鼠标左键按下,无论是否真的开始拖拽,都会执行一次 grabToImage。这是一个相对昂贵的操作(涉及 GPU 到 CPU 的纹理读取)。如果用户频繁点击图标,会造成不必要的性能开销。
    • 改进建议:延迟执行 grabToImage,直到确认用户确实在进行拖拽操作(即鼠标移动超过阈值)。

4. 代码安全

  • 空指针/生命周期风险
    • 现状:直接操作 root.Drag
    • 风险:如果 root 对象在拖拽过程中被异步销毁,访问 root.Drag 可能会导致段错误(C++ 层崩溃)。原代码使用 dndItem 正是为了规避这个问题。
    • 改进建议:确保拖拽代理对象的生命周期长于视图项的生命周期,或者使用 ObjectOwnership 等机制保护对象不被过早回收。

综合改进建议代码

为了解决上述逻辑、性能和潜在安全问题,建议对代码进行如下调整:

Control {
    id: root
    // 使用 visible 控制显示,opacity 控制拖拽时的透明度
    visible: !mouseArea.drag.active
    opacity: mouseArea.drag.active ? 0.5 : 1 

    // ... 其他属性保持不变 ...

    // 建议保留或重新引入一个稳定的拖拽代理对象,例如 dndItem
    // Item { id: dndItem; visible: false; ... }

    Drag.dragType: Drag.Automatic
    // 绑定到 MouseArea 的拖拽状态,而不是直接绑定 MouseArea 对象
    Drag.active: mouseArea.drag.active
    Drag.hotSpot.x: width / 2
    Drag.hotSpot.y: height / 2

    states: State {
        name: "dragged";
        when: mouseArea.drag.active
        PropertyChanges {
            target: root
            // 保持原有的重置位置逻辑
        }
    }

    background: Rectangle {
        // ...
        AppIcon {
            id: appIcon
            // ...
        }

        MouseArea {
            id: mouseArea
            anchors.fill: parent
            hoverEnabled: false // 如果不需要悬停效果,保持 false
            acceptedButtons: Qt.LeftButton
            enabled: root.dndEnabled
            
            // 防止拖拽本身改变 root 的 x/y,除非这是预期行为
            // 如果仅用于触发 Drag 附加属性,建议 drag.target: null 或 undefined
            // 但原代码逻辑似乎允许拖拽改变位置,这里保持原样
            drag.target: root 
            
            // 记录按下的初始位置,用于计算阈值
            property point pressPos: Qt.point(0, 0)

            onPressed: function (mouse) {
                if (mouse.button === Qt.LeftButton && root.dndEnabled) {
                    pressPos = Qt.point(mouse.x, mouse.y)
                }
            }

            onPositionChanged: function (mouse) {
                if (!mouseArea.drag.active && root.dndEnabled) {
                    // 计算移动距离
                    var dx = mouse.x - pressPos.x
                    var dy = mouse.y - pressPos.y
                    // 设置拖拽阈值,例如 5 像素
                    if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
                        // 确认开始拖拽,执行抓取和启动
                        appIcon.grabToImage(function(result) {
                            // 安全起见,检查 root 是否仍然有效
                            if (root) {
                                // 如果使用了 dndItem,这里应该操作 dndItem.Drag
                                root.Drag.imageSource = result.url
                                // 启动拖拽
                                root.Drag.active = true
                            }
                        })
                    }
                }
            }
            
            onReleased: {
                // 拖拽结束,重置状态
                root.Drag.active = false
            }
        }
    }
}

总结

这段代码的变更试图简化拖拽逻辑,但引入了严重的回归风险(崩溃)和交互逻辑缺陷(点击即抓图)。建议:

  1. 恢复或重新实现 dndItem 代理机制,确保拖拽期间对象安全。
  2. 修正拖拽触发逻辑,增加移动距离阈值判断,避免点击时执行昂贵的图像抓取。
  3. 确认 drag.target: root 的意图。如果只是想触发 DnD 数据传输,通常不需要设置 drag.target,或者设置为一个临时的代理 Item;如果是为了在视图中移动图标位置,则需配合 states 中的逻辑确保位置正确。目前的实现可能导致拖拽时图标位置跳动或不可控。

@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

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

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

@18202781743
Copy link
Contributor Author

/forcemerge

@deepin-bot
Copy link

deepin-bot bot commented Mar 5, 2026

This pr force merged! (status: blocked)

@deepin-bot deepin-bot bot merged commit 91727a5 into linuxdeepin:master Mar 5, 2026
9 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.

3 participants