Handle the wheel event in the wasm window

Align the wheel event handling with other events - move the handler to
wasm window and create a C++ wrapper class for the js wheel event.

Fixes: QTBUG-109622
Change-Id: I915e502de7c0784ec9a6745a90ddcda062e91b2b
Reviewed-by: Mikołaj Boc <Mikolaj.Boc@qt.io>
Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
Mikolaj Boc 2022-12-23 12:48:13 +01:00
parent 5d356ae2bd
commit c54416a06c
6 changed files with 94 additions and 79 deletions

View File

@ -29,19 +29,6 @@ using namespace emscripten;
Q_GUI_EXPORT int qt_defaultDpiX();
bool g_scrollingInvertedFromDevice = false;
static void mouseWheelEvent(emscripten::val event)
{
emscripten::val wheelInverted = event["webkitDirectionInvertedFromDevice"];
if (wheelInverted.as<bool>())
g_scrollingInvertedFromDevice = true;
}
EMSCRIPTEN_BINDINGS(qtMouseModule) {
function("qtMouseWheelEvent", &mouseWheelEvent);
}
QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
: QObject(screen),
m_windowStack(std::bind(&QWasmCompositor::onTopWindowChanged, this)),
@ -77,8 +64,6 @@ void QWasmCompositor::deregisterEventHandlers()
emscripten_set_keydown_callback(screenElementSelector.constData(), 0, 0, NULL);
emscripten_set_keyup_callback(screenElementSelector.constData(), 0, 0, NULL);
emscripten_set_wheel_callback(screenElementSelector.constData(), 0, 0, NULL);
emscripten_set_touchstart_callback(screenElementSelector.constData(), 0, 0, NULL);
emscripten_set_touchend_callback(screenElementSelector.constData(), 0, 0, NULL);
emscripten_set_touchmove_callback(screenElementSelector.constData(), 0, 0, NULL);
@ -94,13 +79,6 @@ void QWasmCompositor::destroy()
void QWasmCompositor::initEventHandlers()
{
if (platform() == Platform::MacOS) {
if (!emscripten::val::global("window")["safari"].isUndefined()) {
screen()->element().call<void>("addEventListener", val("wheel"),
val::module_property("qtMouseWheelEvent"));
}
}
constexpr EM_BOOL UseCapture = 1;
const QByteArray screenElementSelector = screen()->eventTargetId().toUtf8();
@ -109,9 +87,6 @@ void QWasmCompositor::initEventHandlers()
emscripten_set_keyup_callback(screenElementSelector.constData(), (void *)this, UseCapture,
&keyboard_cb);
emscripten_set_wheel_callback(screenElementSelector.constData(), (void *)this, UseCapture,
&wheel_cb);
emscripten_set_touchstart_callback(screenElementSelector.constData(), (void *)this, UseCapture,
&touchCallback);
emscripten_set_touchend_callback(screenElementSelector.constData(), (void *)this, UseCapture,
@ -314,12 +289,6 @@ int QWasmCompositor::keyboard_cb(int eventType, const EmscriptenKeyboardEvent *k
return static_cast<int>(wasmCompositor->processKeyboard(eventType, keyEvent));
}
int QWasmCompositor::wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
{
QWasmCompositor *compositor = (QWasmCompositor *) userData;
return static_cast<int>(compositor->processWheel(eventType, wheelEvent));
}
int QWasmCompositor::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
{
auto compositor = reinterpret_cast<QWasmCompositor*>(userData);
@ -354,51 +323,6 @@ bool QWasmCompositor::processKeyboard(int eventType, const EmscriptenKeyboardEve
: result;
}
bool QWasmCompositor::processWheel(int eventType, const EmscriptenWheelEvent *wheelEvent)
{
Q_UNUSED(eventType);
const EmscriptenMouseEvent* mouseEvent = &wheelEvent->mouse;
int scrollFactor = 0;
switch (wheelEvent->deltaMode) {
case DOM_DELTA_PIXEL:
scrollFactor = 1;
break;
case DOM_DELTA_LINE:
scrollFactor = 12;
break;
case DOM_DELTA_PAGE:
scrollFactor = 20;
break;
};
scrollFactor = -scrollFactor; // Web scroll deltas are inverted from Qt deltas.
Qt::KeyboardModifiers modifiers = KeyboardModifier::getForEvent(*mouseEvent);
QPoint targetPointInScreenCoords =
screen()->mapFromLocal(QPoint(mouseEvent->targetX, mouseEvent->targetY));
QWindow *targetWindow = screen()->compositor()->windowAt(targetPointInScreenCoords, 5);
if (!targetWindow)
return 0;
QPoint pointInTargetWindowCoords = targetWindow->mapFromGlobal(targetPointInScreenCoords);
QPoint pixelDelta;
if (wheelEvent->deltaY != 0) pixelDelta.setY(wheelEvent->deltaY * scrollFactor);
if (wheelEvent->deltaX != 0) pixelDelta.setX(wheelEvent->deltaX * scrollFactor);
QPoint angleDelta = pixelDelta; // FIXME: convert from pixels?
bool accepted = QWindowSystemInterface::handleWheelEvent(
targetWindow, QWasmIntegration::getTimestamp(), pointInTargetWindowCoords,
targetPointInScreenCoords, pixelDelta, angleDelta, modifiers,
Qt::NoScrollPhase, Qt::MouseEventNotSynthesized,
g_scrollingInvertedFromDevice);
return accepted;
}
bool QWasmCompositor::processTouch(int eventType, const EmscriptenTouchEvent *touchEvent)
{
QList<QWindowSystemInterface::TouchPoint> touchPointList;

View File

@ -79,13 +79,10 @@ private:
void deliverUpdateRequest(QWasmWindow *window, UpdateRequestDeliveryType updateType);
static int keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData);
static int focus_cb(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData);
static int wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData);
static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData);
bool processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent);
bool processWheel(int eventType, const EmscriptenWheelEvent *wheelEvent);
bool processTouch(int eventType, const EmscriptenTouchEvent *touchEvent);
void enterWindow(QWindow *window, const QPoint &localPoint, const QPoint &globalPoint);

View File

@ -134,4 +134,45 @@ std::optional<DragEvent> DragEvent::fromWeb(emscripten::val event)
return DragEvent(*eventType, event);
}
WheelEvent::WheelEvent(EventType type, emscripten::val event) : MouseEvent(type, event)
{
deltaMode = ([event]() {
const int deltaMode = event["deltaMode"].as<int>();
const auto jsWheelEventType = emscripten::val::global("WheelEvent");
if (deltaMode == jsWheelEventType["DOM_DELTA_PIXEL"].as<int>())
return DeltaMode::Pixel;
else if (deltaMode == jsWheelEventType["DOM_DELTA_LINE"].as<int>())
return DeltaMode::Line;
return DeltaMode::Page;
})();
delta = QPoint(event["deltaX"].as<int>(), event["deltaY"].as<int>());
webkitDirectionInvertedFromDevice = event["webkitDirectionInvertedFromDevice"].as<bool>();
}
WheelEvent::~WheelEvent() = default;
WheelEvent::WheelEvent(const WheelEvent &other) = default;
WheelEvent::WheelEvent(WheelEvent &&other) = default;
WheelEvent &WheelEvent::operator=(const WheelEvent &other) = default;
WheelEvent &WheelEvent::operator=(WheelEvent &&other) = default;
std::optional<WheelEvent> WheelEvent::fromWeb(emscripten::val event)
{
const auto eventType = ([&event]() -> std::optional<EventType> {
const auto eventTypeString = event["type"].as<std::string>();
if (eventTypeString == "wheel")
return EventType::Wheel;
return std::nullopt;
})();
if (!eventType)
return std::nullopt;
return WheelEvent(*eventType, event);
}
QT_END_NAMESPACE

View File

@ -24,6 +24,7 @@ enum class EventType {
PointerUp,
PointerEnter,
PointerLeave,
Wheel,
};
enum class PointerType {
@ -36,6 +37,8 @@ enum class WindowArea {
Client,
};
enum class DeltaMode { Pixel, Line, Page };
namespace KeyboardModifier {
namespace internal
{
@ -203,6 +206,22 @@ struct DragEvent : public MouseEvent
emscripten::val dataTransfer;
};
struct WheelEvent : public MouseEvent
{
static std::optional<WheelEvent> fromWeb(emscripten::val webEvent);
WheelEvent(EventType type, emscripten::val webEvent);
~WheelEvent();
WheelEvent(const WheelEvent &other);
WheelEvent(WheelEvent &&other);
WheelEvent &operator=(const WheelEvent &other);
WheelEvent &operator=(WheelEvent &&other);
DeltaMode deltaMode;
bool webkitDirectionInvertedFromDevice;
QPoint delta;
};
QT_END_NAMESPACE
#endif // QWASMEVENT_H

View File

@ -89,6 +89,12 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingSt
if (processDrop(*DragEvent::fromWeb(event)))
event.call<void>("preventDefault");
});
m_wheelEventCallback = std::make_unique<qstdweb::EventCallback>(
m_qtWindow, "wheel", [this](emscripten::val event) {
if (processWheel(*WheelEvent::fromWeb(event)))
event.call<void>("preventDefault");
});
}
QWasmWindow::~QWasmWindow()
@ -447,6 +453,30 @@ bool QWasmWindow::processDrop(const DragEvent &event)
return true;
}
bool QWasmWindow::processWheel(const WheelEvent &event)
{
// Web scroll deltas are inverted from Qt deltas - negate.
const int scrollFactor = -([&event]() {
switch (event.deltaMode) {
case DeltaMode::Pixel:
return 1;
case DeltaMode::Line:
return 12;
case DeltaMode::Page:
return 20;
};
})();
const auto pointInScreen = platformScreen()->mapFromLocal(
dom::mapPoint(event.target, platformScreen()->element(), event.localPoint));
return QWindowSystemInterface::handleWheelEvent(
window(), QWasmIntegration::getTimestamp(), mapFromGlobal(pointInScreen), pointInScreen,
event.delta * scrollFactor, event.delta * scrollFactor, event.modifiers,
Qt::NoScrollPhase, Qt::MouseEventNotSynthesized,
event.webkitDirectionInvertedFromDevice);
}
QRect QWasmWindow::normalGeometry() const
{
return m_normalGeometry;

View File

@ -33,6 +33,7 @@ class EventCallback;
class ClientArea;
struct DragEvent;
struct PointerEvent;
struct WheelEvent;
class QWasmWindow final : public QPlatformWindow
{
@ -93,6 +94,7 @@ private:
bool processPointer(const PointerEvent &event);
bool processDrop(const DragEvent &event);
bool processWheel(const WheelEvent &event);
QWindow *m_window = nullptr;
QWasmCompositor *m_compositor = nullptr;
@ -116,6 +118,8 @@ private:
std::unique_ptr<qstdweb::EventCallback> m_dropCallback;
std::unique_ptr<qstdweb::EventCallback> m_wheelEventCallback;
Qt::WindowStates m_state = Qt::WindowNoState;
Qt::WindowStates m_previousWindowState = Qt::WindowNoState;