From 1c7210d60b945cccddc9c701d1a83464bc9bfd09 Mon Sep 17 00:00:00 2001 From: zhaoyingzhen Date: Tue, 10 Mar 2026 15:46:34 +0800 Subject: [PATCH] fix: resolve touch screen scrolling and clicking issues in bluetooth applet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Issue background and cause: Touch screen operations such as scrolling and clicking did not work correctly with the native `QScroller`. - Specific changes: Replaced `QScroller` with a custom `TouchScrollFilter` for the bluetooth applet UI scroll area. - Technical details: Added `TouchScrollFilter` intercepting `QEvent::TouchUpdate` to offset the scroll area manually based on global coordinates differences, and on un-dragged `QEvent::TouchEnd`, simulating a click using QMouseEvent via core app dispatcher. - 问题背景和原因:原生的 `QScroller` 导致蓝牙插件面板的触屏滚动和无拖拽点击操作无法正常工作。 - 具体修改内容:移除了原有蓝牙插件面板滚动区域的 `QScroller` 实现,替换为系统性的 `TouchScrollFilter` 组件。 - 修改的技术细节:通过事件过滤器分析触控变化,基于全屏坐标增量手动控制滚动条数值实现触屏滚动。对于无明显拖拽的触摸抬起事件,通过查询子节点来直接模拟分发鼠标点击事件,从而保证点击的精准响应。 Log: fix: resolve touch screen scrolling and clicking issues in bluetooth applet Pms: BUG-352505 --- .../bluetooth/componments/bluetoothapplet.cpp | 8 +- plugins/dde-dock/common/touchscrollfilter.cpp | 94 +++++++++++++++++++ plugins/dde-dock/common/touchscrollfilter.h | 27 ++++++ 3 files changed, 124 insertions(+), 5 deletions(-) create mode 100644 plugins/dde-dock/common/touchscrollfilter.cpp create mode 100644 plugins/dde-dock/common/touchscrollfilter.h diff --git a/plugins/dde-dock/bluetooth/componments/bluetoothapplet.cpp b/plugins/dde-dock/bluetooth/componments/bluetoothapplet.cpp index ef7be0cf9..4f83ed14c 100644 --- a/plugins/dde-dock/bluetooth/componments/bluetoothapplet.cpp +++ b/plugins/dde-dock/bluetooth/componments/bluetoothapplet.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2016 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2016 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -13,6 +13,7 @@ #include "jumpsettingbutton.h" #include "constants.h" #include "plugins-logging-category.h" +#include "touchscrollfilter.h" #include #include @@ -229,10 +230,7 @@ void BluetoothApplet::initUi() m_scrollArea->setPalette(scrollAreaBackground); // QScroller::grabGesture(m_scrollArea->viewport(), QScroller::LeftMouseButtonGesture); - QScroller *scroller = QScroller::scroller(m_scrollArea); - QScrollerProperties sp; - sp.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff); - scroller->setScrollerProperties(sp); + new TouchScrollFilter(m_scrollArea); m_mainLayout->setSpacing(0); m_mainLayout->setContentsMargins(0, 10, 0, 0); diff --git a/plugins/dde-dock/common/touchscrollfilter.cpp b/plugins/dde-dock/common/touchscrollfilter.cpp new file mode 100644 index 000000000..25e2a9786 --- /dev/null +++ b/plugins/dde-dock/common/touchscrollfilter.cpp @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "touchscrollfilter.h" + +#include +#include +#include +#include +#include + +TouchScrollFilter::TouchScrollFilter(QAbstractScrollArea *area) + : QObject(area) + , m_area(area) + , m_isDrag(false) +{ + if (m_area) { + m_area->viewport()->setAttribute(Qt::WA_AcceptTouchEvents, true); + m_area->setAttribute(Qt::WA_AcceptTouchEvents, true); + m_area->viewport()->installEventFilter(this); + } +} + +bool TouchScrollFilter::eventFilter(QObject *watched, QEvent *event) +{ + if (!m_area) + return QObject::eventFilter(watched, event); + + const QEvent::Type type = event->type(); + if (type != QEvent::TouchBegin && type != QEvent::TouchUpdate && + type != QEvent::TouchEnd && type != QEvent::TouchCancel) { + return QObject::eventFilter(watched, event); + } + + QTouchEvent *te = static_cast(event); + if (te->points().isEmpty()) { + event->accept(); + return true; + } + + QEventPoint pt = te->points().first(); + + switch (type) { + case QEvent::TouchBegin: { + m_lastPos = pt.globalPosition(); + m_isDrag = false; + break; + } + case QEvent::TouchUpdate: { + qreal deltaY = pt.globalPosition().y() - m_lastPos.y(); + qreal deltaX = pt.globalPosition().x() - m_lastPos.x(); + + if (!m_isDrag && (qAbs(deltaY) >= QApplication::startDragDistance() || qAbs(deltaX) >= QApplication::startDragDistance())) { + m_isDrag = true; + } + + if (m_isDrag) { + if (qAbs(deltaY) > 0 && m_area->verticalScrollBar()) { + m_area->verticalScrollBar()->setValue(m_area->verticalScrollBar()->value() - deltaY); + } + if (qAbs(deltaX) > 0 && m_area->horizontalScrollBar()) { + m_area->horizontalScrollBar()->setValue(m_area->horizontalScrollBar()->value() - deltaX); + } + m_lastPos = pt.globalPosition(); + } + break; + } + case QEvent::TouchEnd: { + if (!m_isDrag) { + // Determine the correct target widget for the click + QWidget *target = m_area->viewport(); + QPoint widgetPos = pt.position().toPoint(); + + // If it's a QScrollArea, the content widget is usually a child of the viewport + if (QWidget *child = target->childAt(widgetPos)) { + target = child; + widgetPos = target->mapFrom(m_area->viewport(), widgetPos); + } + + QMouseEvent press(QEvent::MouseButtonPress, widgetPos, pt.globalPosition(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QCoreApplication::sendEvent(target, &press); + QMouseEvent release(QEvent::MouseButtonRelease, widgetPos, pt.globalPosition(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QCoreApplication::sendEvent(target, &release); + } + break; + } + default: + break; + } + + event->accept(); + return true; +} diff --git a/plugins/dde-dock/common/touchscrollfilter.h b/plugins/dde-dock/common/touchscrollfilter.h new file mode 100644 index 000000000..50ba48c36 --- /dev/null +++ b/plugins/dde-dock/common/touchscrollfilter.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef TOUCHSCROLLFILTER_H +#define TOUCHSCROLLFILTER_H + +#include +#include +#include + +class TouchScrollFilter : public QObject +{ +public: + explicit TouchScrollFilter(QAbstractScrollArea *area); + ~TouchScrollFilter() override = default; + +protected: + bool eventFilter(QObject *watched, QEvent *event) override; + +private: + QAbstractScrollArea *m_area; + QPointF m_lastPos; + bool m_isDrag; +}; + +#endif // TOUCHSCROLLFILTER_H