Skip to content

Commit 2d9d16f

Browse files
committed
feat: enhance touch drag support for QML windows in qt6
1. Added touch drag support for QQuickWindow with proper grabber state tracking 2. Implemented multi-screen aware touch drag handling with position validation 3. Added new Utility::updateMousePointForWindowMove overload for custom positions 4. Improved window boundary checks using scene coordinates 5. Added debug logging for window drag operations The changes enable proper touch-based window dragging for QML-based applications while maintaining existing mouse drag functionality. The implementation tracks touch grabber states to determine if QML items have handled the touch events, and only initiates window dragging when no QML items have claimed the touch points. The multi-screen support ensures correct behavior across different display configurations. feat: 增强QML窗口的触摸拖拽支持 1. 为QQuickWindow添加触摸拖拽支持,并正确跟踪grabber状态 2. 实现多屏幕感知的触摸拖拽处理,包含位置验证 3. 新增Utility::updateMousePointForWindowMove重载方法支持自定义位置 4. 使用场景坐标改进窗口边界检查 5. 添加窗口拖拽操作的调试日志 这些变更使得基于QML的应用程序能够正确支持触摸拖拽窗口,同时保留现有的鼠 标拖拽功能。实现通过跟踪触摸grabber状态来判断QML组件是否处理了触摸事件, 仅当没有QML组件处理时才启动窗口拖拽。多屏幕支持确保在不同显示配置下的正 确行为。 pms: BUG-309157
1 parent 14394b0 commit 2d9d16f

File tree

4 files changed

+187
-3
lines changed

4 files changed

+187
-3
lines changed

xcb/dnotitlebarwindowhelper.cpp

Lines changed: 165 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "dplatformintegration.h"
1414

1515
#include <QMouseEvent>
16+
#include <QTouchEvent>
1617
#include <QTimer>
1718
#include <QMetaProperty>
1819
#include <QScreen>
@@ -40,6 +41,9 @@ DNoTitlebarWindowHelper::DNoTitlebarWindowHelper(QWindow *window, quint32 window
4041
if (window->flags().testFlag(Qt::FramelessWindowHint))
4142
window->setFlags(window->flags() & ~Qt::FramelessWindowHint);
4243

44+
// 初始化窗口类型判断结果
45+
m_isQuickWindow = window->inherits("QQuickWindow");
46+
4347
mapped[window] = this;
4448
m_nativeSettingsValid = DPlatformIntegration::buildNativeSettings(this, windowID);
4549
Q_ASSERT(m_nativeSettingsValid);
@@ -524,15 +528,35 @@ bool DNoTitlebarWindowHelper::windowEvent(QEvent *event)
524528
VtableHook::resetVtable(w);
525529
return w->event(event);
526530
}
531+
DNoTitlebarWindowHelper *self = mapped.value(w);
527532

528533
// get touch begin position
529534
static bool isTouchDown = false;
530535
static QPointF touchBeginPosition;
536+
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
537+
static QHash<int, QObject*> touchPointGrabbers; // 新增:记录触摸点的grabber状态
538+
const auto isQuickWindow = self->m_isQuickWindow;
539+
#endif
540+
531541
if (event->type() == QEvent::TouchBegin) {
532542
isTouchDown = true;
543+
// ========== 新增:记录处理前的grabber状态 ==========
544+
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
545+
if (isQuickWindow && event->isPointerEvent()) {
546+
QPointerEvent *pe = static_cast<QPointerEvent*>(event);
547+
touchPointGrabbers.clear();
548+
for (const auto &point : pe->points()) {
549+
QObject *grabber = pe->exclusiveGrabber(point);
550+
touchPointGrabbers[point.id()] = grabber;
551+
}
552+
}
553+
#endif
533554
}
534555
if (event->type() == QEvent::TouchEnd || event->type() == QEvent::MouseButtonRelease) {
535556
isTouchDown = false;
557+
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
558+
touchPointGrabbers.clear(); // 清理grabber记录
559+
#endif
536560
}
537561
if (isTouchDown && event->type() == QEvent::MouseButtonPress) {
538562
touchBeginPosition = static_cast<QMouseEvent*>(event)->globalPos();
@@ -546,7 +570,6 @@ bool DNoTitlebarWindowHelper::windowEvent(QEvent *event)
546570
}
547571
}
548572

549-
DNoTitlebarWindowHelper *self = mapped.value(w);
550573
quint32 winId = self->m_windowID;
551574
bool is_mouse_move = event->type() == QEvent::MouseMove && static_cast<QMouseEvent*>(event)->buttons() == Qt::LeftButton;
552575

@@ -570,10 +593,15 @@ bool DNoTitlebarWindowHelper::windowEvent(QEvent *event)
570593
g_pressPoint[this] = dynamic_cast<QMouseEvent*>(event)->globalPos();
571594
}
572595

596+
// ========== 修改:鼠标事件处理保持原有逻辑 ==========
573597
if (is_mouse_move && !event->isAccepted() && g_pressPoint.contains(this)) {
574598
QMouseEvent *me = static_cast<QMouseEvent*>(event);
575599
QRect windowRect = QRect(QPoint(0, 0), w->size());
576-
if (!windowRect.contains(me->windowPos().toPoint())) {
600+
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
601+
if (!windowRect.contains(me->scenePosition().toPoint())) {
602+
#else
603+
if (!windowRect.contains(me->localPos().toPoint())) {
604+
#endif
577605
return ret;
578606
}
579607

@@ -583,6 +611,7 @@ bool DNoTitlebarWindowHelper::windowEvent(QEvent *event)
583611
}
584612

585613
if (!self->m_windowMoving && self->isEnableSystemMove(winId)) {
614+
qDebug() << "Starting mouse drag for window ID:" << winId;
586615
self->m_windowMoving = true;
587616

588617
event->accept();
@@ -594,6 +623,39 @@ bool DNoTitlebarWindowHelper::windowEvent(QEvent *event)
594623
}
595624
}
596625

626+
// ========== 新增:QML窗口触摸事件的特殊处理 ==========
627+
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
628+
if (isQuickWindow && event->isPointerEvent()) {
629+
QPointerEvent *pe = static_cast<QPointerEvent*>(event);
630+
631+
// 检查是否是触摸事件
632+
bool isTouchEvent = (event->type() == QEvent::TouchBegin ||
633+
event->type() == QEvent::TouchUpdate ||
634+
event->type() == QEvent::TouchEnd);
635+
636+
if (isTouchEvent) {
637+
// 检查是否有新的grabber产生(说明QML Item处理了触摸事件)
638+
bool wasHandledByQML = false;
639+
for (const auto &point : pe->points()) {
640+
QObject *grabberBefore = touchPointGrabbers.value(point.id());
641+
QObject *grabberAfter = pe->exclusiveGrabber(point);
642+
643+
if (grabberBefore != grabberAfter && grabberAfter != nullptr) {
644+
wasHandledByQML = true;
645+
break;
646+
}
647+
}
648+
649+
// 如果没有QML Item处理触摸事件,尝试窗口拖拽
650+
if (!wasHandledByQML && self->isEnableSystemMove(winId)) {
651+
if (self->handleTouchDragForQML(pe)) {
652+
return ret;
653+
}
654+
}
655+
}
656+
}
657+
#endif
658+
597659
return ret;
598660
}
599661

@@ -733,4 +795,105 @@ void DNoTitlebarWindowHelper::updateMoveWindow(quint32 winId)
733795
Utility::updateMousePointForWindowMove(winId);
734796
}
735797

798+
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
799+
bool DNoTitlebarWindowHelper::handleTouchDragForQML(QPointerEvent *touchEvent)
800+
{
801+
QWindow *w = this->m_window;
802+
static QHash<int, QPointF> touchStartPositions;
803+
static QHash<int, bool> touchIsMoving;
804+
static QHash<int, QPointF> lastTouchPositions; // 新增:记录最后的触摸位置用于更新
805+
806+
const auto points = touchEvent->points();
807+
if (points.isEmpty()) {
808+
return false;
809+
}
810+
811+
// 只处理第一个触摸点
812+
const auto &point = points.first();
813+
int touchId = point.id();
814+
815+
// ========== 新增:多屏幕坐标处理 ==========
816+
QScreen *windowScreen = w->screen();
817+
QPointF globalPos = point.globalPosition();
818+
QPointF localPos = point.position();
819+
820+
switch (touchEvent->type()) {
821+
case QEvent::TouchBegin: {
822+
touchStartPositions[touchId] = globalPos;
823+
lastTouchPositions[touchId] = globalPos;
824+
touchIsMoving[touchId] = false;
825+
return false; // 不消费事件,让QML先处理
826+
}
827+
828+
case QEvent::TouchUpdate: {
829+
if (!touchStartPositions.contains(touchId)) {
830+
return false;
831+
}
832+
833+
QPointF startPos = touchStartPositions[touchId];
834+
QPointF currentPos = globalPos;
835+
QPointF delta = currentPos - startPos;
836+
qreal distance = delta.manhattanLength();
837+
qreal threshold = QGuiApplication::styleHints()->startDragDistance();
838+
839+
// 更新最后触摸位置
840+
lastTouchPositions[touchId] = currentPos;
841+
842+
// 检查移动距离是否达到拖拽阈值
843+
if (!touchIsMoving[touchId] && distance >= threshold) {
844+
845+
// ========== 修改:多屏幕环境下的窗口边界检查 ==========
846+
QRect windowRect = QRect(QPoint(0, 0), w->size());
847+
QPoint localTouchPos = localPos.toPoint();
848+
bool inBounds = windowRect.contains(localTouchPos);
849+
850+
// 使用本地坐标检查(更可靠)
851+
if (!inBounds) {
852+
return false;
853+
}
854+
855+
// 开始窗口移动
856+
if (isEnableSystemMove(m_windowID)) {
857+
qDebug() << "Starting touch drag for QML window ID:" << m_windowID
858+
<< "on screen:" << (windowScreen ? windowScreen->name() : "unknown");
859+
m_windowMoving = true;
860+
touchIsMoving[touchId] = true;
861+
862+
startMoveWindow(m_windowID);
863+
touchEvent->accept();
864+
return true;
865+
}
866+
} else if (touchIsMoving[touchId] && m_windowMoving) {
867+
// ========== 修改:使用带坐标参数的updateMousePointForWindowMove ==========
868+
Utility::updateMousePointForWindowMove(m_windowID, currentPos.toPoint());
869+
touchEvent->accept();
870+
return true;
871+
}
872+
break;
873+
}
874+
875+
case QEvent::TouchEnd: {
876+
if (touchIsMoving.value(touchId, false)) {
877+
m_windowMoving = false;
878+
// ========== 修改:使用最后的触摸位置结束窗口移动 ==========
879+
QPointF lastPos = lastTouchPositions.value(touchId, globalPos);
880+
Utility::updateMousePointForWindowMove(m_windowID, lastPos.toPoint(), true);
881+
qDebug() << "Finished touch drag for QML window ID:" << m_windowID;
882+
}
883+
884+
// 清理状态
885+
touchStartPositions.remove(touchId);
886+
touchIsMoving.remove(touchId);
887+
lastTouchPositions.remove(touchId);
888+
break;
889+
}
890+
891+
default:
892+
break;
893+
}
894+
895+
return false;
896+
}
897+
#endif
898+
736899
DPP_END_NAMESPACE

xcb/dnotitlebarwindowhelper.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515

1616
QT_BEGIN_NAMESPACE
1717
class QWindow;
18+
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
19+
class QPointerEvent;
20+
#endif
1821
QT_END_NAMESPACE
1922

2023
DPP_BEGIN_NAMESPACE
@@ -104,6 +107,14 @@ private slots:
104107
bool isEnableSystemMove(quint32 winId);
105108
bool updateWindowBlurAreasForWM();
106109
void updateWindowShape();
110+
111+
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
112+
// 新增:QML窗口触摸拖拽处理函数 (仅Qt6)
113+
bool handleTouchDragForQML(QPointerEvent *touchEvent);
114+
#else
115+
// Qt5版本的触摸拖拽处理函数
116+
bool handleTouchDragForQML(QTouchEvent *touchEvent);
117+
#endif
107118

108119
void onWindowSizeChanged();
109120

@@ -114,6 +125,7 @@ private slots:
114125
quint32 m_windowID;
115126
bool m_windowMoving = false;
116127
bool m_nativeSettingsValid = false;
128+
bool m_isQuickWindow = false; // 新增:缓存窗口类型判断结果
117129

118130
QVector<Utility::BlurArea> m_blurAreaList;
119131
QList<QPainterPath> m_blurPathList;

xcb/utility.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ class Utility
5454
// 导致窗口无法移动。此处跟deepin-wm配合,使用其它方式通知窗管鼠标位置更新了
5555
// TODO: kwin适配udpate之后没有结束move状态,新增一个finished参数,当传入true时通知kwin结束
5656
static void updateMousePointForWindowMove(quint32 WId, bool finished = false);
57+
// 新增:支持多屏幕的版本,接受自定义全局坐标
58+
static void updateMousePointForWindowMove(quint32 WId, const QPoint &globalPos, bool finished = false);
5759

5860
static void showWindowSystemMenu(quint32 WId, QPoint globalPos = QPoint());
5961
static void setFrameExtents(WId wid, const QMargins &margins);

xcb/utility_x11.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,15 @@ void Utility::cancelWindowMoveResize(quint32 WId)
168168

169169
void Utility::updateMousePointForWindowMove(quint32 WId, bool finished/* = false*/)
170170
{
171-
xcb_client_message_event_t xev;
171+
// 获取主屏幕的光标位置并调用重载版本
172172
const QPoint &globalPos = qApp->primaryScreen()->handle()->cursor()->pos();
173+
updateMousePointForWindowMove(WId, globalPos, finished);
174+
}
175+
176+
// 新增:支持多屏幕的版本,接受自定义全局坐标
177+
void Utility::updateMousePointForWindowMove(quint32 WId, const QPoint &globalPos, bool finished/* = false*/)
178+
{
179+
xcb_client_message_event_t xev;
173180

174181
xev.response_type = XCB_CLIENT_MESSAGE;
175182
xev.type = internAtom("_DEEPIN_MOVE_UPDATE");

0 commit comments

Comments
 (0)