summaryrefslogtreecommitdiffstats
path: root/src/gui/kernel/qguiapplication.cpp
diff options
context:
space:
mode:
authorGatis Paeglis <gatis.paeglis@qt.io>2017-08-30 11:03:39 +0200
committerGatis Paeglis <gatis.paeglis@qt.io>2017-10-10 22:09:07 +0000
commita37785ec7638e7485112b87dd7e767881fecc114 (patch)
treec238af4d154c12e80532daff5245627b181f5d40 /src/gui/kernel/qguiapplication.cpp
parent0900cf3581be3ff2b2e924ce0d845566c5df841e (diff)
qpa: enhance mouse event with type and button data
... and deprecate QWSI APIs that accepts mouse event without mouse button/ type data. In the early days of Qt5 it was decided to centralize mouse button/type handling in QGuiApplication (because of limitation of some now unknown platform). This has proven to be problematic as mouse handling details differ across platforms (e.g on X11 we do not receive mouse release event when closing popup windows or ordinary windows that are closed from the mouse press event). Instead of hacking around platform specific behaviors in Qt Gui, we should move this task back to platform plugins (similar to how this was done in Qt4 with native APIs sending mouse details directly to QApplication). There are even cases where it simply is not possible to deduce (from QGuiApplication) which button caused the event (e.g. when more than one button is involved and some event goes missing). Besisdes, throwing away information which is already available at QPA level (for free) and trying to deduce it again at Qt Gui level seems impractical, fagile (as probably noticed by people fixing all the unexpected issues) and adds unnecessary complexity. Note: Removing the deprecated QWSI APIs from offscreen plugin depends on fixing autotests that rely on QOffscreenCursor::setPos() logic. For the convenience of testing use QT_QPA_DISABLE_ENHANCED_MOUSE to restore to the old code path where QGuiApplication does the mouse state deducing. Other platforms have similar issues. I do not have all supported platform available on my desk, so other platform maintainers will need to take care of porting those platforms to the new APIs. And mainly, I don't want to deal with all the hacks that other platforms have added to workaround this broken mouse logic. In Qt6 we need to remove deprecated code path from QGuiApplication. This patch: - Extends QWindowSystemInterfacePrivate::MouseEvent ctor with QEvent::Type and Qt::MouseButton. We use this extra data when processing mouse events in QGuiApplication. This actually is similar to KeyEvent, where we do pass the type (press or release) to QtGui. - Refactors QGuiApplicationPrivate::processMouseEvent and qtestlib to use the new APIs. Task-number: QTBUG-59277 Task-number: QTBUG-62329 Task-number: QTBUG-63467 Change-Id: If94fd46a7cccfea8264dcb1368804c73334558b8 Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
Diffstat (limited to 'src/gui/kernel/qguiapplication.cpp')
-rw-r--r--src/gui/kernel/qguiapplication.cpp151
1 files changed, 98 insertions, 53 deletions
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index 77897e9e57..80a6a9c1e4 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -1842,29 +1842,111 @@ void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePriv
}
}
+/*! \internal
+
+ History is silent on why Qt splits mouse events that change position and
+ button state at the same time. We believe that this was done to emulate mouse
+ behavior on touch screens. If mouse tracking is enabled, we will get move
+ events before the button is pressed. A touch panel does not generally give
+ move events when not pressed, so without event splitting code path we would
+ only see a press in a new location without any intervening moves. This could
+ confuse code that is written for a real mouse. The same is true for mouse
+ release events that change position, see tst_QWidget::touchEventSynthesizedMouseEvent()
+ auto test.
+*/
void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent *e)
{
- QEvent::Type type;
- Qt::MouseButtons stateChange = e->buttons ^ mouse_buttons;
- if (e->globalPos != QGuiApplicationPrivate::lastCursorPosition && (stateChange != Qt::NoButton)) {
- // A mouse event should not change both position and buttons at the same time. Instead we
- // should first send a move event followed by a button changed event. Since this is not the case
- // with the current event, we split it in two.
- QWindowSystemInterfacePrivate::MouseEvent mouseButtonEvent(e->window.data(), e->timestamp,
- e->localPos, e->globalPos, e->buttons, e->modifiers, e->source, e->nonClientArea);
- if (e->flags & QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic)
- mouseButtonEvent.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic;
- e->buttons = mouse_buttons;
- processMouseEvent(e);
- processMouseEvent(&mouseButtonEvent);
- return;
+ QEvent::Type type = QEvent::None;
+ Qt::MouseButton button = Qt::NoButton;
+ QWindow *window = e->window.data();
+ bool positionChanged = QGuiApplicationPrivate::lastCursorPosition != e->globalPos;
+ bool mouseMove = false;
+ bool mousePress = false;
+
+ if (e->enhancedMouseEvent()) {
+ type = e->buttonType;
+ button = e->button;
+
+ if (type == QEvent::NonClientAreaMouseMove || type == QEvent::MouseMove)
+ mouseMove = true;
+ else if (type == QEvent::NonClientAreaMouseButtonPress || type == QEvent::MouseButtonPress)
+ mousePress = true;
+
+ if (!mouseMove && positionChanged) {
+ QWindowSystemInterfacePrivate::MouseEvent moveEvent(window, e->timestamp,
+ e->localPos, e->globalPos, e->buttons & ~button, e->modifiers, Qt::NoButton,
+ e->nonClientArea ? QEvent::NonClientAreaMouseMove : QEvent::MouseMove,
+ e->source, e->nonClientArea);
+ if (e->synthetic())
+ moveEvent.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic;
+ processMouseEvent(&moveEvent); // mouse move excluding state change
+ processMouseEvent(e); // the original mouse event
+ return;
+ }
+ } else {
+ Qt::MouseButtons stateChange = e->buttons ^ mouse_buttons;
+ if (positionChanged && (stateChange != Qt::NoButton)) {
+ QWindowSystemInterfacePrivate::MouseEvent moveEvent(window, e->timestamp, e->localPos,
+ e->globalPos, mouse_buttons, e->modifiers, Qt::NoButton, QEvent::None, e->source,
+ e->nonClientArea);
+ if (e->synthetic())
+ moveEvent.flags |= QWindowSystemInterfacePrivate::WindowSystemEvent::Synthetic;
+ processMouseEvent(&moveEvent); // mouse move excluding state change
+ processMouseEvent(e); // the original mouse event
+ return;
+ }
+
+ // In the compatibility path we deduce event type and button that caused the event
+ if (positionChanged) {
+ mouseMove = true;
+ type = e->nonClientArea ? QEvent::NonClientAreaMouseMove : QEvent::MouseMove;
+ } else {
+ // Check to see if a new button has been pressed/released.
+ for (uint mask = Qt::LeftButton; mask <= Qt::MaxMouseButton; mask <<= 1) {
+ if (stateChange & mask) {
+ button = Qt::MouseButton(mask);
+ break;
+ }
+ }
+ if (button == Qt::NoButton) {
+ // Ignore mouse events that don't change the current state. This shouldn't
+ // really happen, getting here can only mean that the stored button state
+ // is out of sync with the actual physical button state.
+ return;
+ }
+ if (button & e->buttons) {
+ mousePress = true;
+ type = e->nonClientArea ? QEvent::NonClientAreaMouseButtonPress
+ : QEvent::MouseButtonPress;
+ } else {
+ type = e->nonClientArea ? QEvent::NonClientAreaMouseButtonRelease
+ : QEvent::MouseButtonRelease;
+ }
+ }
}
- QWindow *window = e->window.data();
modifier_buttons = e->modifiers;
-
QPointF localPoint = e->localPos;
QPointF globalPoint = e->globalPos;
+ bool doubleClick = false;
+
+ if (mouseMove) {
+ QGuiApplicationPrivate::lastCursorPosition = globalPoint;
+ if (qAbs(globalPoint.x() - mousePressX) > mouse_double_click_distance||
+ qAbs(globalPoint.y() - mousePressY) > mouse_double_click_distance)
+ mousePressButton = Qt::NoButton;
+ } else {
+ mouse_buttons = e->buttons;
+ if (mousePress) {
+ ulong doubleClickInterval = static_cast<ulong>(QGuiApplication::styleHints()->mouseDoubleClickInterval());
+ doubleClick = e->timestamp - mousePressTime < doubleClickInterval && button == mousePressButton;
+ mousePressTime = e->timestamp;
+ mousePressButton = button;
+ const QPoint point = QGuiApplicationPrivate::lastCursorPosition.toPoint();
+ mousePressX = point.x();
+ mousePressY = point.y();
+ }
+ }
if (e->nullWindow()) {
window = QGuiApplication::topLevelAt(globalPoint.toPoint());
@@ -1885,43 +1967,6 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
}
}
- Qt::MouseButton button = Qt::NoButton;
- bool doubleClick = false;
-
- if (QGuiApplicationPrivate::lastCursorPosition != globalPoint) {
- type = e->nonClientArea ? QEvent::NonClientAreaMouseMove : QEvent::MouseMove;
- QGuiApplicationPrivate::lastCursorPosition = globalPoint;
- if (qAbs(globalPoint.x() - mousePressX) > mouse_double_click_distance||
- qAbs(globalPoint.y() - mousePressY) > mouse_double_click_distance)
- mousePressButton = Qt::NoButton;
- } else { // Check to see if a new button has been pressed/released.
- for (int check = Qt::LeftButton;
- check <= int(Qt::MaxMouseButton);
- check = check << 1) {
- if (check & stateChange) {
- button = Qt::MouseButton(check);
- break;
- }
- }
- if (button == Qt::NoButton) {
- // Ignore mouse events that don't change the current state.
- return;
- }
- mouse_buttons = e->buttons;
- if (button & e->buttons) {
- ulong doubleClickInterval = static_cast<ulong>(QGuiApplication::styleHints()->mouseDoubleClickInterval());
- doubleClick = e->timestamp - mousePressTime < doubleClickInterval && button == mousePressButton;
- type = e->nonClientArea ? QEvent::NonClientAreaMouseButtonPress : QEvent::MouseButtonPress;
- mousePressTime = e->timestamp;
- mousePressButton = button;
- const QPoint point = QGuiApplicationPrivate::lastCursorPosition.toPoint();
- mousePressX = point.x();
- mousePressY = point.y();
- } else {
- type = e->nonClientArea ? QEvent::NonClientAreaMouseButtonRelease : QEvent::MouseButtonRelease;
- }
- }
-
if (!window)
return;