Skip to content

fix: wheel event not propagating to parent in Qt6 - #770

Draft
18202781743 wants to merge 1 commit into
masterfrom
agent/developer/4798f28a
Draft

fix: wheel event not propagating to parent in Qt6#770
18202781743 wants to merge 1 commit into
masterfrom
agent/developer/4798f28a

Conversation

@18202781743

@18202781743 18202781743 commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

背景 / Background

Qt6 的 QApplication::notify 在派发 wheel event 前执行 we.setAccepted(true)。若控件的 wheelEvent 不调用父类事件函数(最终 event->ignore()),事件将保持 accepted 状态,不会向父控件(如 QScrollArea)传播——这与 Qt5 行为不一致(Qt5 中未处理的滚轮事件默认可传播)。

导致所有 dtk6widget 应用中,位于 QScrollArea / DScrollArea 内的 DSpinBox / DDoubleSpinBox / DComboBox / DSlider 在鼠标悬停滚动时会阻断页面滚动(值被误改 / 选项被切换,但页面不滚动)。复现场景:打印预览高级设置、设置对话框、examples/collections 示例等。

根因 / Root Cause

  • DSpinBox(继承 QSpinBox)、DDoubleSpinBox(继承 QDoubleSpinBox)、DComboBox(继承 QComboBox)均未重写 wheelEvent,沿用各自基类的 wheelEvent(调用 stepBy() / 切项 + event->accept()),在 Qt6 下事件被 accept 后不传播。
  • DSlider 继承结构不同:继承 QWidget,内部持有 SpecialSlider : public QSlider,并通过 qApp->installEventFilter 拦截内部 slider 的滚轮事件,原逻辑在 Qt6 下同样阻断传播。
  • 参考既有修复 fe16c8e9settings/ComboBox::wheelEvent):有焦点时调基类 wheelEvent,无焦点时 return QWidget::wheelEvent(e)(即 ignore())让事件传播。

改动 / Changes

沿用 fe16c8e9 的成熟模式,对 4 个控件统一修复(共 5 个文件):

  1. DSpinBox / DDoubleSpinBoxinclude/widgets/dspinbox.h + src/widgets/dspinbox.cpp

    • 重写 wheelEvent:有焦点 → 调 QSpinBox / QDoubleSpinBox::wheelEvent(保留滚轮调值);无焦点 → QWidget::wheelEventignore() 传播给 scrollarea)。
  2. DComboBoxinclude/widgets/dcombobox.h + src/widgets/dcombobox.cpp

    • 重写 wheelEvent:有焦点 → 调 QComboBox::wheelEvent(保留滚轮切项);无焦点 → QWidget::wheelEvent(传播)。
  3. DSlidersrc/widgets/dslider.cpp

    • 因继承结构不同,在其 eventFilter 中处理:内部 slider 无焦点时 e->ignore() + return true,在 Qt6 下取消 accept 使事件传播给父控件,等价于 fe16c8e9 模式。

行为 / Behavior

控件 焦点 修改前(Qt6) 修改后
spinbox / combobox / slider 阻断滚动 ❌ 页面滚动 ✅
spinbox / combobox / slider 调值 / 切项 调值 / 切项(不变)
  • 恢复 Qt5 行为:鼠标悬停无焦点时滚轮滚动页面;点击聚焦后滚轮调值 / 切项。
  • readOnly spinbox 聚焦后滚动:基类自身 ignore(),事件正常传播,无需额外判断。
  • DLineEdit / DFileChooserEdit 不受影响(QLineEdit 不重写 wheelEvent,默认 ignore()),未改动。
  • Qt5 / Qt6 跨版本兼容:仅用公开 API,无 #if 条件编译。对 Qt5 无影响(本就正常),修复 Qt6 回归。

自测 / Test

  • 编译验证(DTK6 / Qt6):dspinbox.cpp / dcombobox.cpp / dslider.cpp 均编译通过,零 error / warning。
  • 待带界面测试机验证:
    1. examples/collections 中 spinbox / combobox / slider 在 scrollarea 内 → 无焦点滚轮滚动页面,有焦点滚轮调值 / 切项。
    2. 打印预览 Ctrl+P → 展开高级设置 → 滚轮经过页边距 / 缩放 / 份数 / 水印等控件时页面正常滚动。
    3. 点击控件聚焦后滚轮 → 保留原有行为(无回归)。

关联 / References

⚠️ Draft PR,待审核,请勿合并。

Summary by Sourcery

Adjust wheel event handling for DTK widgets so unfocused controls no longer block parent scroll areas, restoring expected scroll behavior in Qt6.

Bug Fixes:

  • Ensure DSpinBox and DDoubleSpinBox propagate wheel events to parent scroll areas when not focused instead of always consuming them.
  • Ensure DComboBox propagates wheel events when not focused so page scrolling works as expected in scrollable containers.
  • Update DSlider wheel event filtering so only focused sliders respond to the mouse wheel and unfocused ones allow scroll events to bubble up.

Qt6 QApplication::notify pre-accepts wheel events (we.setAccepted(true))
before delivery. If a widget's wheelEvent doesn't call the parent class
(which would ignore() the event), the event stays accepted and does not
propagate to parent widgets (e.g. QScrollArea). This differs from Qt5
where unhandled wheel events propagated by default.

Fix by overriding wheelEvent in DSpinBox, DDoubleSpinBox, DComboBox
following the pattern from commit fe16c8e (settings/ComboBox):
  - hasFocus(): call base class wheelEvent (stepBy + accept, default behavior)
  - no focus:  call QWidget::wheelEvent (ignore() -> event propagates)

For DSlider, which is a QWidget containing an inner QSlider with an
eventFilter on qApp, fix the eventFilter instead: when the inner slider
has no focus, call e->ignore() before returning true so the event
propagates to the parent scrollarea in Qt6.

This fixes the Qt6-specific regression where scrolling over spinbox /
combobox / slider inside a scrollarea was blocked.

Ref: DDE-72
@deepin-ci-robot

Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@deepin-ci-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: 18202781743

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 14, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR adjusts wheel event handling for DSpinBox, DDoubleSpinBox, DComboBox, and DSlider so that mouse wheel scrolling only affects the control when it has focus and otherwise propagates to parent scroll areas, restoring Qt5-like behavior under Qt6.

Sequence diagram for wheel event handling with focus-dependent propagation

sequenceDiagram
    actor User
    participant DSpinBox
    participant QSpinBox
    participant QWidget
    participant QScrollArea

    User->>DSpinBox: wheelEvent
    alt hasFocus
        DSpinBox->>QSpinBox: wheelEvent(event)
        QSpinBox-->>DSpinBox: accept
        DSpinBox-->>User: value changed
    else noFocus
        DSpinBox->>QWidget: wheelEvent(event)
        QWidget-->>DSpinBox: ignore
        DSpinBox-->>QScrollArea: wheelEvent
        QScrollArea-->>User: scroll page
    end
Loading

Sequence diagram for DSlider eventFilter wheel event propagation

sequenceDiagram
    actor User
    participant DSlider
    participant SpecialSlider
    participant QScrollArea

    User->>SpecialSlider: wheelEvent
    SpecialSlider->>DSlider: eventFilter(watched,e)
    alt mouseWheelEnabled and hasFocus
        DSlider-->>SpecialSlider: return false
        SpecialSlider-->>User: QSlider::wheelEvent handles
    else notEnabled or noFocus
        DSlider->>SpecialSlider: e->ignore()
        DSlider-->>SpecialSlider: return true
        SpecialSlider-->>QScrollArea: wheelEvent
        QScrollArea-->>User: scroll page
    end
Loading

File-Level Changes

Change Details Files
Override wheel event handling in DSpinBox and DDoubleSpinBox so value changes occur only when the widget has focus and wheel events otherwise propagate to parent widgets.
  • Add QWheelEvent include to the spinbox implementation file.
  • Declare protected wheelEvent overrides in DSpinBox and DDoubleSpinBox headers.
  • Implement wheelEvent in both classes to delegate to the base spinbox when focused and to QWidget when not focused.
src/widgets/dspinbox.cpp
include/widgets/dspinbox.h
Override wheel event handling in DComboBox to change selection only when the combo box has focus and propagate wheel events otherwise.
  • Add QWheelEvent include to the combobox implementation file.
  • Declare a protected wheelEvent override in the DComboBox header.
  • Implement wheelEvent to delegate to QComboBox when focused and to QWidget when not focused.
src/widgets/dcombobox.cpp
include/widgets/dcombobox.h
Adjust DSlider’s event filter wheel handling so that the inner QSlider only processes wheel events when focused and the events otherwise propagate to parent scroll areas.
  • Update the wheel event branch in DSlider::eventFilter to check both mouse wheel enable state and focus on the inner slider.
  • Allow normal QSlider wheel handling when wheel usage is enabled and the slider has focus.
  • Ignore wheel events and consume them in the filter when the inner slider lacks focus so that Qt6 will propagate them to parent widgets.
src/widgets/dslider.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

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