Deliver non-client area mouse events to WASM windows

QWasmCompositor now delivers non-client area mouse events to windows as
it should, which fixes a lot of issues with window manipulation. One of
such issues is re-docking of dock widgets.

Fixes: QTBUG-105092
Change-Id: I9de45b7e1b1a80b64387031eb0cc0b31a4be2571
Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
This commit is contained in:
Mikolaj Boc 2022-07-20 14:29:06 +02:00
parent 434595865b
commit 19507dc678
6 changed files with 81 additions and 29 deletions

View File

@ -939,8 +939,6 @@ bool QWasmCompositor::processPointer(const PointerEvent& event)
const QPoint targetPointInScreenCoords = screen()->geometry().topLeft() + event.point; const QPoint targetPointInScreenCoords = screen()->geometry().topLeft() + event.point;
QEvent::Type buttonEventType = QEvent::None;
QWindow *const targetWindow = ([this, &targetPointInScreenCoords]() -> QWindow * { QWindow *const targetWindow = ([this, &targetPointInScreenCoords]() -> QWindow * {
auto *targetWindow = auto *targetWindow =
m_windowManipulation.operation() == WindowManipulation::Operation::None ? m_windowManipulation.operation() == WindowManipulation::Operation::None ?
@ -968,7 +966,6 @@ bool QWasmCompositor::processPointer(const PointerEvent& event)
switch (event.type) { switch (event.type) {
case EventType::PointerDown: case EventType::PointerDown:
{ {
buttonEventType = QEvent::MouseButtonPress;
if (targetWindow) if (targetWindow)
targetWindow->requestActivate(); targetWindow->requestActivate();
@ -981,8 +978,6 @@ bool QWasmCompositor::processPointer(const PointerEvent& event)
} }
case EventType::PointerUp: case EventType::PointerUp:
{ {
buttonEventType = QEvent::MouseButtonRelease;
m_windowManipulation.onPointerUp(event); m_windowManipulation.onPointerUp(event);
if (m_pressedWindow) { if (m_pressedWindow) {
@ -997,8 +992,6 @@ bool QWasmCompositor::processPointer(const PointerEvent& event)
} }
case EventType::PointerMove: case EventType::PointerMove:
{ {
buttonEventType = QEvent::MouseMove;
if (wasmTargetWindow && event.mouseButtons.testFlag(Qt::NoButton)) { if (wasmTargetWindow && event.mouseButtons.testFlag(Qt::NoButton)) {
const bool isOnResizeRegion = wasmTargetWindow->isPointOnResizeRegion(targetPointInScreenCoords); const bool isOnResizeRegion = wasmTargetWindow->isPointOnResizeRegion(targetPointInScreenCoords);
@ -1033,24 +1026,51 @@ bool QWasmCompositor::processPointer(const PointerEvent& event)
leaveWindow(m_lastMouseTargetWindow); leaveWindow(m_lastMouseTargetWindow);
} }
bool shouldDeliverEvent = pointerIsWithinTargetWindowBounds; const bool eventAccepted = deliverEventToTarget(event, targetWindow);
QWindow *eventTarget = targetWindow;
if (!eventTarget && event.type == EventType::PointerUp) {
eventTarget = m_lastMouseTargetWindow;
m_lastMouseTargetWindow = nullptr;
shouldDeliverEvent = true;
}
const bool eventAccepted =
eventTarget != nullptr && shouldDeliverEvent &&
QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(
eventTarget, QWasmIntegration::getTimestamp(), pointInTargetWindowCoords, targetPointInScreenCoords,
event.mouseButtons, event.mouseButton, buttonEventType, event.modifiers);
if (!eventAccepted && event.type == EventType::PointerDown) if (!eventAccepted && event.type == EventType::PointerDown)
QGuiApplicationPrivate::instance()->closeAllPopups(); QGuiApplicationPrivate::instance()->closeAllPopups();
return eventAccepted; return eventAccepted;
} }
bool QWasmCompositor::deliverEventToTarget(const PointerEvent &event, QWindow *eventTarget)
{
const QPoint pointInScreenCoords = screen()->geometry().topLeft() + event.point;
const QPoint targetPointClippedToScreen(
std::max(screen()->geometry().left(),
std::min(screen()->geometry().right(), pointInScreenCoords.x())),
std::max(screen()->geometry().top(),
std::min(screen()->geometry().bottom(), pointInScreenCoords.y())));
bool deliveringToPreviouslyClickedWindow = false;
if (!eventTarget) {
if (event.type != EventType::PointerUp || !m_lastMouseTargetWindow)
return false;
eventTarget = m_lastMouseTargetWindow;
m_lastMouseTargetWindow = nullptr;
deliveringToPreviouslyClickedWindow = true;
}
WindowArea windowArea = WindowArea::Client;
if (!eventTarget->geometry().contains(targetPointClippedToScreen)
&& !deliveringToPreviouslyClickedWindow) {
if (!eventTarget->frameGeometry().contains(targetPointClippedToScreen))
return false;
windowArea = WindowArea::NonClient;
}
const QEvent::Type eventType =
MouseEvent::mouseEventTypeFromEventType(event.type, windowArea);
return eventType != QEvent::None &&
QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(
eventTarget, QWasmIntegration::getTimestamp(),
eventTarget->mapFromGlobal(targetPointClippedToScreen),
targetPointClippedToScreen, event.mouseButtons, event.mouseButton,
eventType, event.modifiers);
}
QWasmCompositor::WindowManipulation::WindowManipulation(QWasmScreen *screen) QWasmCompositor::WindowManipulation::WindowManipulation(QWasmScreen *screen)
: m_screen(screen) : m_screen(screen)
{ {
@ -1124,14 +1144,10 @@ void QWasmCompositor::WindowManipulation::onPointerMove(
if (operation() == Operation::None || event.pointerId != m_state->pointerId) if (operation() == Operation::None || event.pointerId != m_state->pointerId)
return; return;
const auto pointInScreenCoords = m_screen->geometry().topLeft() + event.point;
switch (operation()) { switch (operation()) {
case Operation::Move: { case Operation::Move: {
const QPoint targetPointClippedToScreen( const QPoint targetPointClippedToScreen =
std::max(m_screen->geometry().left(), std::min(m_screen->geometry().right(), pointInScreenCoords.x())), m_screen->translateAndClipGlobalPoint(event.point);
std::max(m_screen->geometry().top(), std::min(m_screen->geometry().bottom(), pointInScreenCoords.y())));
const QPoint difference = targetPointClippedToScreen - const QPoint difference = targetPointClippedToScreen -
std::get<MoveState>(m_state->operationSpecific).m_lastPointInScreenCoords; std::get<MoveState>(m_state->operationSpecific).m_lastPointInScreenCoords;
@ -1141,6 +1157,7 @@ void QWasmCompositor::WindowManipulation::onPointerMove(
break; break;
} }
case Operation::Resize: { case Operation::Resize: {
const auto pointInScreenCoords = m_screen->geometry().topLeft() + event.point;
resizeWindow(pointInScreenCoords - resizeWindow(pointInScreenCoords -
std::get<ResizeState>(m_state->operationSpecific).m_originInScreenCoords); std::get<ResizeState>(m_state->operationSpecific).m_originInScreenCoords);
break; break;

View File

@ -208,6 +208,7 @@ private:
static int wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData); static int wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData);
bool processPointer(const PointerEvent& event); bool processPointer(const PointerEvent& event);
bool deliverEventToTarget(const PointerEvent& event, QWindow *eventTarget);
static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData); static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData);

View File

@ -8,6 +8,7 @@
#include <QtCore/qglobal.h> #include <QtCore/qglobal.h>
#include <QtCore/qnamespace.h> #include <QtCore/qnamespace.h>
#include <QtGui/qevent.h>
#include <QPoint> #include <QPoint>
@ -29,6 +30,11 @@ enum class PointerType {
Other, Other,
}; };
enum class WindowArea {
NonClient,
Client,
};
namespace KeyboardModifier { namespace KeyboardModifier {
namespace internal namespace internal
{ {
@ -129,7 +135,24 @@ struct Q_CORE_EXPORT MouseEvent : public Event
static constexpr Qt::MouseButtons buttonsFromWeb(unsigned short webButtons) { static constexpr Qt::MouseButtons buttonsFromWeb(unsigned short webButtons) {
// Coincidentally, Qt and web bitfields match. // Coincidentally, Qt and web bitfields match.
return Qt::MouseButtons::fromInt(webButtons); return Qt::MouseButtons::fromInt(webButtons);
} }
static constexpr QEvent::Type mouseEventTypeFromEventType(
EventType eventType, WindowArea windowArea) {
switch (eventType) {
case EventType::PointerDown :
return windowArea == WindowArea::Client ?
QEvent::MouseButtonPress : QEvent::NonClientAreaMouseButtonPress;
case EventType::PointerUp :
return windowArea == WindowArea::Client ?
QEvent::MouseButtonRelease : QEvent::NonClientAreaMouseButtonRelease;
case EventType::PointerMove :
return windowArea == WindowArea::Client ?
QEvent::MouseMove : QEvent::NonClientAreaMouseMove;
default:
return QEvent::None;
}
}
}; };
struct Q_CORE_EXPORT PointerEvent : public MouseEvent struct Q_CORE_EXPORT PointerEvent : public MouseEvent

View File

@ -263,6 +263,15 @@ QWindow *QWasmScreen::topLevelAt(const QPoint &p) const
return m_compositor->windowAt(p); return m_compositor->windowAt(p);
} }
QPoint QWasmScreen::translateAndClipGlobalPoint(const QPoint &p) const
{
return QPoint(
std::max(screen()->geometry().left(),
std::min(screen()->geometry().right(), screen()->geometry().left() + p.x())),
std::max(screen()->geometry().top(),
std::min(screen()->geometry().bottom(), screen()->geometry().top() + p.y())));
}
void QWasmScreen::invalidateSize() void QWasmScreen::invalidateSize()
{ {
m_geometry = QRect(); m_geometry = QRect();

View File

@ -53,6 +53,8 @@ public:
QWindow *topWindow() const; QWindow *topWindow() const;
QWindow *topLevelAt(const QPoint &p) const override; QWindow *topLevelAt(const QPoint &p) const override;
QPoint translateAndClipGlobalPoint(const QPoint &p) const;
void invalidateSize(); void invalidateSize();
void updateQScreenAndCanvasRenderSize(); void updateQScreenAndCanvasRenderSize();
void installCanvasResizeObserver(); void installCanvasResizeObserver();

View File

@ -1079,14 +1079,14 @@ void QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent *event)
if (state == nullptr || !state->dragging) if (state == nullptr || !state->dragging)
break; break;
#ifndef Q_OS_MAC #if !defined(Q_OS_MAC) && !defined(Q_OS_WASM)
if (state->nca) { if (state->nca) {
endDrag(); endDrag();
} }
#endif #endif
break; break;
case QEvent::NonClientAreaMouseButtonRelease: case QEvent::NonClientAreaMouseButtonRelease:
#ifdef Q_OS_MAC #if defined(Q_OS_MAC) || defined(Q_OS_WASM)
if (state) if (state)
endDrag(); endDrag();
#endif #endif