Skip to content

fix: filter synthesized click after wheel/gesture to prevent trackpad flicker - #180

Open
Resurgamz wants to merge 1 commit into
linuxdeepin:develop/eaglefrom
Resurgamz:develop/eagle
Open

fix: filter synthesized click after wheel/gesture to prevent trackpad flicker#180
Resurgamz wants to merge 1 commit into
linuxdeepin:develop/eaglefrom
Resurgamz:develop/eagle

Conversation

@Resurgamz

@Resurgamz Resurgamz commented Aug 13, 2026

Copy link
Copy Markdown

根因分析

PMS #279597 看图触摸板双指缩放闪烁:LibImageGraphicsView::mousePressEventimagegraphicsview.cpp:1063)对任意按下无条件 emit clicked(),不区分 MouseEventSynthesizedByQt 合成事件、不对滚轮/手势去抖。触摸板双指缩放后手指离开,Qt 合成的鼠标 press 命中此路径,误触发查看模式切换,最终经 resizeEventsetScaleValue(1.0) 还原默认大小。同文件 mouseReleaseEvent:1029/1049 既有 source 过滤 + 200ms 去抖,press 侧缺失。

关键证据:

  1. mousePressEvent:1074 无条件 emit clicked()(与 mouseReleaseEvent:1029/1049 的过滤+去抖不对称)
  2. event():1207 未接纳触摸事件致 Qt 合成鼠标事件
  3. 症状不对称性自洽:合成点击对放大/缩小对称触发,仅放大时 setScaleValue(1.0) 可见回弹

修复方案

press/release 两侧对"滚轮/缩放手势结束后 300ms 内的合成点击"去抖过滤,与既有 mouseReleaseEvent 过滤模式同构:

  1. 新增成员 m_lastWheelOrGestureTime,在 wheelEvent / pinchTriggered GestureFinished 中记录时间戳
  2. mousePressEvent 中若 e->source() == Qt::MouseEventSynthesizedByQt 且 300ms 内 → 提前 return
  3. mouseReleaseEvent 合成分支的 sigClicked() 发射增加同样的时间窗判断

改动安全评估

低风险:局部 early-return + 时间戳记录,无函数签名变更、无公开 API 变更。仅影响合成事件路径,真实鼠标点击与真实单指触摸翻页不受影响。blame 确认 emit clicked() 来自 first commit(非历史 bug 修复),无回归风险。

Summary by Sourcery

Debounce and filter synthesized mouse clicks following wheel and pinch gestures to prevent unintended view mode toggling and trackpad zoom flicker.

Bug Fixes:

  • Ignore synthesized mouse press events occurring shortly after wheel or pinch-gesture completion to avoid accidental click handling.
  • Prevent click emission on mouse release when it occurs within a short window after recent wheel or gesture activity.

… flicker

1. 新增成员 m_lastWheelOrGestureTime 记录最近一次滚轮/缩放手势时间戳;
2. 在 wheelEvent 和 pinchTriggered GestureFinished 中记录时间戳;
3. mousePressEvent 中过滤手势结束后 300ms 内的合成点击,避免误触发查看模式切换;
4. mouseReleaseEvent 合成分支的 sigClicked 发射增加同样的时间窗判断;

=====================================

1. added member m_lastWheelOrGestureTime to record last wheel/gesture timestamp;
2. recorded timestamp in wheelEvent and pinchTriggered GestureFinished;
3. filtered synthesized click within 300ms after gesture in mousePressEvent to avoid spurious view mode toggle;
4. added same time window check for sigClicked emission in mouseReleaseEvent synthesized branch;

Log: 修复触摸板双指缩放后手指离开合成点击导致图片闪烁还原默认大小的问题,过滤手势结束后的合成点击

PMS: BUG-279597
Bug: https://pms.uniontech.com/bug-view-279597.html

@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 @Resurgamz, 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: Resurgamz

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

@sourcery-ai

sourcery-ai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Debounces and filters synthesized mouse clicks generated shortly after wheel or pinch gestures in LibImageGraphicsView by tracking the last wheel/gesture timestamp and applying it symmetrically to mouse press/release handling, preventing unintended view mode toggles on trackpad zoom.

Sequence diagram for debounced synthesized click after wheel/pinch gestures

sequenceDiagram
    actor User
    participant QtEventSystem
    participant LibImageGraphicsView

    User->>QtEventSystem: trackpad pinch / wheel
    QtEventSystem->>LibImageGraphicsView: wheelEvent(event)
    LibImageGraphicsView->>LibImageGraphicsView: m_lastWheelOrGestureTime = currentMSecsSinceEpoch()

    User->>QtEventSystem: end gesture
    QtEventSystem->>LibImageGraphicsView: pinchTriggered(gesture)
    LibImageGraphicsView->>LibImageGraphicsView: m_lastWheelOrGestureTime = currentMSecsSinceEpoch() (GestureFinished)

    QtEventSystem->>LibImageGraphicsView: mousePressEvent(e)
    alt synthesized mouse event within 300ms
        LibImageGraphicsView->>LibImageGraphicsView: [e->source() == MouseEventSynthesizedByQt && now - m_lastWheelOrGestureTime < 300]
        LibImageGraphicsView-->>QtEventSystem: e->accept(), return
    else real or delayed click
        LibImageGraphicsView->>LibImageGraphicsView: normal press handling
    end

    QtEventSystem->>LibImageGraphicsView: mouseReleaseEvent(e)
    alt click within 200ms and small movement and not near gesture
        LibImageGraphicsView->>LibImageGraphicsView: [now - m_clickTime < 200 && abs(xpos) < 50 && now - m_lastWheelOrGestureTime >= 300]
        LibImageGraphicsView->>QtEventSystem: emit sigClicked()
    else ignore as gesture artifact
        LibImageGraphicsView->>LibImageGraphicsView: no sigClicked()
    end
Loading

File-Level Changes

Change Details Files
Introduce a shared timestamp for the last wheel or pinch gesture and use it to ignore synthesized clicks that occur shortly after zoom interactions.
  • Add member m_lastWheelOrGestureTime to LibImageGraphicsView to store the timestamp of the last wheel or pinch gesture.
  • Update wheelEvent to record the current time in m_lastWheelOrGestureTime whenever a wheel event occurs.
  • Update pinchTriggered to record the current time in m_lastWheelOrGestureTime when a pinch gesture reaches GestureFinished state.
libimageviewer/viewpanel/scen/imagegraphicsview.h
libimageviewer/viewpanel/scen/imagegraphicsview.cpp
Extend mouse press/release click detection logic to debounce synthesized clicks within 300ms of the last wheel or pinch gesture.
  • Add an early-return in mousePressEvent that accepts and discards MouseEventSynthesizedByQt events occurring less than 300ms after m_lastWheelOrGestureTime.
  • Tighten the existing click-detection condition in mouseReleaseEvent by requiring that at least 300ms have passed since m_lastWheelOrGestureTime before emitting sigClicked().
libimageviewer/viewpanel/scen/imagegraphicsview.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

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

★ 总体评分:95分

■ 【总体评价】

代码实现了触摸板与触摸屏缩放手势及滚轮操作后合成点击事件的过滤,但存在魔法数字问题
逻辑正确且有效解决误触问题,因新增魔法数字未提取为常量扣5分

■ 【详细分析】

  • 1.语法逻辑(基本正确)✓
    mousePressEventmouseReleaseEvent 中增加基于时间戳的过滤条件,并在 pinchTriggeredwheelEvent 中准确更新时间戳,形成了完整的防御链路。wheelEvent 在函数入口处更新时间戳,即使后续因加载状态提前返回,时间戳也已正确记录,不影响防误触逻辑。
    潜在问题:mouseReleaseEvent 中连续两次调用了 QDateTime::currentMSecsSinceEpoch(),虽然时间差极小不会导致逻辑错误,但存在理论上的时间不一致微小瑕疵。
    建议:将 QDateTime::currentMSecsSinceEpoch() 的结果缓存到局部变量中复用。
  • 2.代码质量(良好)✓
    新增成员变量命名 m_lastWheelOrGestureTime 符合 Qt 的 m_ 前缀规范,且添加了清晰的中文注释说明其用途与防御目的,代码可读性良好。
    潜在问题:代码中硬编码了 30020050 等魔法数字,特别是新增的 300 毫秒阈值未定义为具名常量,降低了代码的可维护性。
    建议:在类定义或匿名命名空间中将这些阈值定义为 static constexpr qint64 常量,例如 kClickIntervalThresholdkMoveOffsetThresholdkGestureFilterInterval
  • 3.代码性能(高效)✓
    修改仅引入了极低开销的整数比较和系统时间获取操作,对图形视图的渲染与事件处理性能无显著影响。
    建议:结合语法逻辑中的建议,通过局部变量缓存时间戳减少一次系统调用。
  • 4.代码安全(存在0个安全漏洞)✓
    漏洞对比统计:新增漏洞 0 个,减少漏洞 0 个,持平 0 个
    本次修改仅涉及 UI 交互层面的时间戳比较与事件拦截,不涉及内存越界、命令注入、权限提升等安全攻击面,变量类型使用 qint64 不会导致溢出,整体安全。
  • 建议:继续保持对 UI 事件源的安全校验意识,防止未来引入不可信的输入源。

■ 【改进建议代码示例】

// 文件: imagegraphicsview.h
private:
    // 定义交互判定阈值常量
    static constexpr qint64 kClickIntervalThreshold = 200;
    static constexpr int kMoveOffsetThreshold = 50;
    static constexpr qint64 kGestureFilterInterval = 300;

    //单击时间
    qint64 m_clickTime{0};
    //最近一次滚轮/缩放手势的时间戳,用于过滤手势结束后合成的点击
    qint64 m_lastWheelOrGestureTime{0};

// 文件: imagegraphicsview.cpp
void LibImageGraphicsView::mouseReleaseEvent(QMouseEvent *e)
{
    // ... 前置逻辑 ...
    const qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
    if ((currentTime - m_clickTime) < kClickIntervalThreshold && abs(xpos) < kMoveOffsetThreshold &&
            (currentTime - m_lastWheelOrGestureTime) >= kGestureFilterInterval) {
        m_clickTime = currentTime;
        emit sigClicked();
    }
    // ... 后置逻辑 ...
}

void LibImageGraphicsView::mousePressEvent(QMouseEvent *e)
{
    // 过滤触摸板/触摸屏缩放手势结束后短时间内的合成点击,避免误触发查看模式切换
    if (e->source() == Qt::MouseEventSynthesizedByQt &&
            (QDateTime::currentMSecsSinceEpoch() - m_lastWheelOrGestureTime) < kGestureFilterInterval) {
        e->accept();
        return;
    }
#ifdef tablet_PC
    m_press = true;
#endif
    // ... 后置逻辑 ...
}

void LibImageGraphicsView::pinchTriggered(QPinchGesture *gesture)
{
    // ... 前置逻辑 ...
    if (gesture->state() == Qt::GestureFinished) {
        m_isFirstPinch = false;
        m_lastWheelOrGestureTime = QDateTime::currentMSecsSinceEpoch();
        gesture->setCenterPoint(m_centerPoint);
        return;
    }
    // ... 后置逻辑 ...
}

void LibImageGraphicsView::wheelEvent(QWheelEvent *event)
{
    // 记录滚轮时间戳,用于过滤手势结束后短时间内的合成点击
    m_lastWheelOrGestureTime = QDateTime::currentMSecsSinceEpoch();

    // 加载过程不可缩放
    if (m_spinner && m_spinner->isVisible()) {
        return;
    }
    // ... 后置逻辑 ...
}

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.

2 participants