qtbase/src/plugins/platforms/wasm/qwasmwindowclientarea.cpp
Lorn Potter 8375adcbe4 wasm: add dragleave event handling
Fixes: QTBUG-129149
Change-Id: I946f43e3a696c801a60a9a209a70ccaf57252a60
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
Reviewed-by: Piotr Wierciński <piotr.wiercinski@qt.io>
(cherry picked from commit 8a93093271329f1fb867c0627e63f61ed7330860)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
2024-10-11 01:03:25 +00:00

203 lines
8.9 KiB
C++

// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qwasmwindowclientarea.h"
#include "qwasmdom.h"
#include "qwasmevent.h"
#include "qwasmscreen.h"
#include "qwasmwindow.h"
#include "qwasmdrag.h"
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpointingdevice.h>
#include <QtCore/qassert.h>
QT_BEGIN_NAMESPACE
ClientArea::ClientArea(QWasmWindow *window, QWasmScreen *screen, emscripten::val element)
: m_screen(screen), m_window(window), m_element(element)
{
const auto callback = std::function([this](emscripten::val event) {
processPointer(*PointerEvent::fromWeb(event));
event.call<void>("preventDefault");
event.call<void>("stopPropagation");
});
m_pointerDownCallback =
std::make_unique<qstdweb::EventCallback>(element, "pointerdown", callback);
m_pointerMoveCallback =
std::make_unique<qstdweb::EventCallback>(element, "pointermove", callback);
m_pointerUpCallback = std::make_unique<qstdweb::EventCallback>(element, "pointerup", callback);
m_pointerCancelCallback =
std::make_unique<qstdweb::EventCallback>(element, "pointercancel", callback);
element.call<void>("setAttribute", emscripten::val("draggable"), emscripten::val("true"));
m_dragStartCallback = std::make_unique<qstdweb::EventCallback>(
element, "dragstart", [this](emscripten::val webEvent) {
webEvent.call<void>("preventDefault");
auto event = *DragEvent::fromWeb(webEvent, m_window->window());
QWasmDrag::instance()->onNativeDragStarted(&event);
});
m_dragOverCallback = std::make_unique<qstdweb::EventCallback>(
element, "dragover", [this](emscripten::val webEvent) {
webEvent.call<void>("preventDefault");
auto event = *DragEvent::fromWeb(webEvent, m_window->window());
QWasmDrag::instance()->onNativeDragOver(&event);
});
m_dropCallback = std::make_unique<qstdweb::EventCallback>(
element, "drop", [this](emscripten::val webEvent) {
webEvent.call<void>("preventDefault");
auto event = *DragEvent::fromWeb(webEvent, m_window->window());
QWasmDrag::instance()->onNativeDrop(&event);
});
m_dragEndCallback = std::make_unique<qstdweb::EventCallback>(
element, "dragend", [this](emscripten::val webEvent) {
webEvent.call<void>("preventDefault");
auto event = *DragEvent::fromWeb(webEvent, m_window->window());
QWasmDrag::instance()->onNativeDragFinished(&event);
});
m_dragLeaveCallback = std::make_unique<qstdweb::EventCallback>(
element, "dragleave", [this](emscripten::val webEvent) {
webEvent.call<void>("preventDefault");
auto event = *DragEvent::fromWeb(webEvent, m_window->window());
QWasmDrag::instance()->onNativeDragLeave(&event);
});
}
bool ClientArea::processPointer(const PointerEvent &event)
{
switch (event.type) {
case EventType::PointerDown:
m_element.call<void>("setPointerCapture", event.pointerId);
if ((m_window->window()->flags() & Qt::WindowDoesNotAcceptFocus)
!= Qt::WindowDoesNotAcceptFocus
&& m_window->window()->isTopLevel())
m_window->window()->requestActivate();
break;
case EventType::PointerUp:
m_element.call<void>("releasePointerCapture", event.pointerId);
break;
default:
break;
};
const bool eventAccepted = deliverEvent(event);
if (!eventAccepted && event.type == EventType::PointerDown)
QGuiApplicationPrivate::instance()->closeAllPopups();
return eventAccepted;
}
bool ClientArea::deliverEvent(const PointerEvent &event)
{
const auto pointInScreen = m_screen->mapFromLocal(
dom::mapPoint(event.target(), m_screen->element(), event.localPoint));
const auto geometryF = m_screen->geometry().toRectF();
const QPointF targetPointClippedToScreen(
qBound(geometryF.left(), pointInScreen.x(), geometryF.right()),
qBound(geometryF.top(), pointInScreen.y(), geometryF.bottom()));
if (event.pointerType == PointerType::Mouse) {
const QEvent::Type eventType =
MouseEvent::mouseEventTypeFromEventType(event.type, WindowArea::Client);
return eventType != QEvent::None
&& QWindowSystemInterface::handleMouseEvent(
m_window->window(), QWasmIntegration::getTimestamp(),
m_window->window()->mapFromGlobal(targetPointClippedToScreen),
targetPointClippedToScreen, event.mouseButtons, event.mouseButton,
eventType, event.modifiers);
}
if (event.pointerType == PointerType::Pen) {
qreal pressure;
switch (event.type) {
case EventType::PointerDown :
case EventType::PointerMove :
pressure = event.pressure;
break;
case EventType::PointerUp :
pressure = 0.0;
break;
default:
return false;
}
// Tilt in the browser is in the range +-90, but QTabletEvent only goes to +-60.
qreal xTilt = qBound(-60.0, event.tiltX, 60.0);
qreal yTilt = qBound(-60.0, event.tiltY, 60.0);
// Barrel rotation is reported as 0 to 359, but QTabletEvent wants a signed value.
qreal rotation = event.twist > 180.0 ? 360.0 - event.twist : event.twist;
return QWindowSystemInterface::handleTabletEvent(
m_window->window(), QWasmIntegration::getTimestamp(), m_screen->tabletDevice(),
m_window->window()->mapFromGlobal(targetPointClippedToScreen),
targetPointClippedToScreen, event.mouseButtons, pressure, xTilt, yTilt,
event.tangentialPressure, rotation, event.modifiers);
}
QWindowSystemInterface::TouchPoint *touchPoint;
QPointF pointInTargetWindowCoords =
QPointF(m_window->window()->mapFromGlobal(targetPointClippedToScreen));
QPointF normalPosition(pointInTargetWindowCoords.x() / m_window->window()->width(),
pointInTargetWindowCoords.y() / m_window->window()->height());
const auto tp = m_pointerIdToTouchPoints.find(event.pointerId);
if (event.pointerType != PointerType::Pen && tp != m_pointerIdToTouchPoints.end()) {
touchPoint = &tp.value();
} else {
touchPoint = &m_pointerIdToTouchPoints
.insert(event.pointerId, QWindowSystemInterface::TouchPoint())
.value();
// Assign touch point id. TouchPoint::id is int, but QGuiApplicationPrivate::processTouchEvent()
// will not synthesize mouse events for touch points with negative id; use the absolute value for
// the touch point id.
touchPoint->id = qAbs(event.pointerId);
touchPoint->state = QEventPoint::State::Pressed;
}
const bool stationaryTouchPoint = (normalPosition == touchPoint->normalPosition);
touchPoint->normalPosition = normalPosition;
touchPoint->area = QRectF(targetPointClippedToScreen, QSizeF(event.width, event.height))
.translated(-event.width / 2, -event.height / 2);
touchPoint->pressure = event.pressure;
switch (event.type) {
case EventType::PointerUp:
touchPoint->state = QEventPoint::State::Released;
break;
case EventType::PointerMove:
touchPoint->state = (stationaryTouchPoint ? QEventPoint::State::Stationary
: QEventPoint::State::Updated);
break;
default:
break;
}
QList<QWindowSystemInterface::TouchPoint> touchPointList;
touchPointList.reserve(m_pointerIdToTouchPoints.size());
std::transform(m_pointerIdToTouchPoints.begin(), m_pointerIdToTouchPoints.end(),
std::back_inserter(touchPointList),
[](const QWindowSystemInterface::TouchPoint &val) { return val; });
if (event.type == EventType::PointerUp)
m_pointerIdToTouchPoints.remove(event.pointerId);
return event.type == EventType::PointerCancel
? QWindowSystemInterface::handleTouchCancelEvent(
m_window->window(), QWasmIntegration::getTimestamp(), m_screen->touchDevice(),
event.modifiers)
: QWindowSystemInterface::handleTouchEvent(
m_window->window(), QWasmIntegration::getTimestamp(), m_screen->touchDevice(),
touchPointList, event.modifiers);
}
QT_END_NAMESPACE