Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwasmevent.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qwasmevent.h"
5
7
8#include <QtCore/private/qmakearray_p.h>
9#include <QtCore/private/qstringiterator_p.h>
10#include <QtCore/qregularexpression.h>
11
13
14namespace {
15constexpr std::string_view WebDeadKeyValue = "Dead";
16
17bool isDeadKeyEvent(const char *key)
18{
19 return qstrncmp(key, WebDeadKeyValue.data(), WebDeadKeyValue.size()) == 0;
20}
21
22Qt::Key getKeyFromCode(const std::string &code)
23{
24 if (auto mapping = QWasmKeyTranslator::mapWebKeyTextToQtKey(code.c_str()))
25 return *mapping;
26
27 static QRegularExpression regex(QString(QStringLiteral(R"re((?:Key|Digit)(\w))re")));
28 const auto codeQString = QString::fromStdString(code);
29 const auto match = regex.match(codeQString);
30
31 if (!match.hasMatch())
32 return Qt::Key_unknown;
33
34 constexpr size_t CharacterIndex = 1;
35 return static_cast<Qt::Key>(match.capturedView(CharacterIndex).at(0).toLatin1());
36}
37
38Qt::Key webKeyToQtKey(const std::string &code, const std::string &key, bool isDeadKey,
39 QFlags<Qt::KeyboardModifier> modifiers)
40{
41 if (isDeadKey) {
42 auto mapped = getKeyFromCode(code);
43 switch (mapped) {
44 case Qt::Key_U:
45 return Qt::Key_Dead_Diaeresis;
46 case Qt::Key_E:
47 return Qt::Key_Dead_Acute;
48 case Qt::Key_I:
49 return Qt::Key_Dead_Circumflex;
50 case Qt::Key_N:
51 return Qt::Key_Dead_Tilde;
52 case Qt::Key_QuoteLeft:
53 return modifiers.testFlag(Qt::ShiftModifier) ? Qt::Key_Dead_Tilde : Qt::Key_Dead_Grave;
54 case Qt::Key_6:
55 return Qt::Key_Dead_Circumflex;
56 case Qt::Key_Apostrophe:
57 return modifiers.testFlag(Qt::ShiftModifier) ? Qt::Key_Dead_Diaeresis
58 : Qt::Key_Dead_Acute;
59 case Qt::Key_AsciiTilde:
60 return Qt::Key_Dead_Tilde;
61 default:
62 return Qt::Key_unknown;
63 }
64 } else if (auto mapping = QWasmKeyTranslator::mapWebKeyTextToQtKey(key.c_str())) {
65 if (modifiers.testFlag(Qt::ShiftModifier) && (*mapping == Qt::Key::Key_Tab))
66 *mapping = Qt::Key::Key_Backtab;
67 return *mapping;
68 }
69
70 // cast to unicode key
71 QString str = QString::fromUtf8(key.c_str()).toUpper();
72 if (str.length() > 1)
73 return Qt::Key_unknown;
74
75 QStringIterator i(str);
76 return static_cast<Qt::Key>(i.next(0));
77}
78
79QFlags<Qt::KeyboardModifier> getKeyboardModifiers(const emscripten::val &event)
80{
81 QFlags<Qt::KeyboardModifier> keyModifier = Qt::NoModifier;
82 if (event["shiftKey"].as<bool>())
83 keyModifier |= Qt::ShiftModifier;
84 if (event["ctrlKey"].as<bool>())
85 keyModifier |= platform() == Platform::MacOS ? Qt::MetaModifier : Qt::ControlModifier;
86 if (event["altKey"].as<bool>())
87 keyModifier |= Qt::AltModifier;
88 if (event["metaKey"].as<bool>())
89 keyModifier |= platform() == Platform::MacOS ? Qt::ControlModifier : Qt::MetaModifier;
90 if (event["constructor"]["name"].as<std::string>() == "KeyboardEvent" &&
91 event["location"].as<unsigned int>() == DOM_KEY_LOCATION_NUMPAD) {
92 keyModifier |= Qt::KeypadModifier;
93 }
94 return keyModifier;
95}
96
97} // namespace
98
99Event::Event(EventType type, emscripten::val webEvent)
100 : webEvent(webEvent), type(type)
101{
102}
103
105{
106 // Check event target via composedPath, which returns the true path even
107 // if the browser retargets the event for Qt's shadow DOM container. This
108 // is needed to avoid capturing the pointer in cases where foreign html
109 // elements are embedded inside Qt's shadow DOM.
110 emscripten::val path = webEvent.call<emscripten::val>("composedPath");
111 QString topElementClassName = QString::fromEcmaString(path[0]["className"]);
112 return topElementClassName.startsWith("qt-"); // .e.g. qt-window-canvas
113}
114
115KeyEvent::KeyEvent(EventType type, emscripten::val event, QWasmDeadKeySupport *deadKeySupport) : Event(type, event)
116{
117 const auto code = event["code"].as<std::string>();
118 const auto webKey = event["key"].as<std::string>();
119 deadKey = isDeadKeyEvent(webKey.c_str());
120 autoRepeat = event["repeat"].as<bool>();
121 modifiers = getKeyboardModifiers(event);
122 key = webKeyToQtKey(code, webKey, deadKey, modifiers);
123
124 text = QString::fromUtf8(webKey);
125
126 // Alt + keypad number -> insert utf-8 character
127 // The individual numbers shall not be inserted but
128 // on some platforms they are if numlock is
129 // activated
130 if ((modifiers & Qt::AltModifier) && (modifiers & Qt::KeypadModifier))
131 text.clear();
132
133 if (text.size() > 1)
134 text.clear();
135
136 if (key == Qt::Key_Tab)
137 text = "\t";
138
139 deadKeySupport->applyDeadKeyTranslations(this);
140}
141
142MouseEvent::MouseEvent(EventType type, emscripten::val event) : Event(type, event)
143{
144 mouseButton = MouseEvent::buttonFromWeb(event["button"].as<int>());
145 mouseButtons = MouseEvent::buttonsFromWeb(event["buttons"].as<unsigned short>());
146 // The current button state (event.buttons) may be out of sync for some PointerDown
147 // events where the "down" state is very brief, for example taps on Apple trackpads.
148 // Qt expects that the current button state is in sync with the event, so we sync
149 // it up here.
150 if (type == EventType::PointerDown)
151 mouseButtons |= mouseButton;
152 localPoint = QPointF(event["offsetX"].as<qreal>(), event["offsetY"].as<qreal>());
153 pointInPage = QPointF(event["pageX"].as<qreal>(), event["pageY"].as<qreal>());
154 pointInViewport = QPointF(event["clientX"].as<qreal>(), event["clientY"].as<qreal>());
155 modifiers = getKeyboardModifiers(event);
156}
157
158PointerEvent::PointerEvent(EventType type, emscripten::val event) : MouseEvent(type, event)
159{
160 pointerId = event["pointerId"].as<int>();
161 pointerType = ([type = event["pointerType"].as<std::string>()]() {
162 if (type == "mouse")
163 return PointerType::Mouse;
164 if (type == "touch")
165 return PointerType::Touch;
166 if (type == "pen")
167 return PointerType::Pen;
168 return PointerType::Other;
169 })();
170 width = event["width"].as<qreal>();
171 height = event["height"].as<qreal>();
172 pressure = event["pressure"].as<qreal>();
173 tiltX = event["tiltX"].as<qreal>();
174 tiltY = event["tiltY"].as<qreal>();
175 tangentialPressure = event["tangentialPressure"].as<qreal>();
176 twist = event["twist"].as<qreal>();
177 isPrimary = event["isPrimary"].as<bool>();
178}
179
180DragEvent::DragEvent(EventType type, emscripten::val event, QWindow *window)
181 : MouseEvent(type, event), dataTransfer(event["dataTransfer"]), targetWindow(window)
182{
183 dropAction = ([event]() {
184 const std::string effect = event["dataTransfer"]["dropEffect"].as<std::string>();
185
186 if (effect == "copy")
187 return Qt::CopyAction;
188 else if (effect == "move")
189 return Qt::MoveAction;
190 else if (effect == "link")
191 return Qt::LinkAction;
192 return Qt::IgnoreAction;
193 })();
194}
195
197{
198 Q_ASSERT_X(type == EventType::DragStart, Q_FUNC_INFO, "Only supported for DragStart");
199 webEvent.call<void>("preventDefault");
200}
201
203{
204 Q_ASSERT_X(type == EventType::DragOver, Q_FUNC_INFO, "Only supported for DragOver");
205 webEvent.call<void>("preventDefault");
206}
207
209{
210 Q_ASSERT_X(type == EventType::Drop, Q_FUNC_INFO, "Only supported for Drop");
211 webEvent.call<void>("preventDefault");
212}
213
214WheelEvent::WheelEvent(EventType type, emscripten::val event) : MouseEvent(type, event)
215{
216 deltaMode = ([event]() {
217 const int deltaMode = event["deltaMode"].as<int>();
218 const auto jsWheelEventType = emscripten::val::global("WheelEvent");
219 if (deltaMode == jsWheelEventType["DOM_DELTA_PIXEL"].as<int>())
220 return DeltaMode::Pixel;
221 else if (deltaMode == jsWheelEventType["DOM_DELTA_LINE"].as<int>())
222 return DeltaMode::Line;
223 return DeltaMode::Page;
224 })();
225
226 delta = QPointF(event["deltaX"].as<qreal>(), event["deltaY"].as<qreal>());
227
228 webkitDirectionInvertedFromDevice = event["webkitDirectionInvertedFromDevice"].as<bool>();
229}
230
231QT_END_NAMESPACE
void applyDeadKeyTranslations(KeyEvent *event)
QFlags< Qt::KeyboardModifier > getKeyboardModifiers(const emscripten::val &event)
constexpr std::string_view WebDeadKeyValue
bool isDeadKeyEvent(const char *key)
Qt::Key webKeyToQtKey(const std::string &code, const std::string &key, bool isDeadKey, QFlags< Qt::KeyboardModifier > modifiers)
Qt::Key getKeyFromCode(const std::string &code)
DeltaMode
Definition qwasmevent.h:53
PointerType
Definition qwasmevent.h:41
EventType
Definition qwasmevent.h:24
DragEvent(EventType type, emscripten::val webEvent, QWindow *targetQWindow)
void acceptDrop()
void acceptDragOver()
void cancelDragStart()
QWindow * targetWindow
Definition qwasmevent.h:150
bool isTargetedForQtElement() const
Event(EventType type, emscripten::val webEvent)
EventType type
Definition qwasmevent.h:62
bool deadKey
Definition qwasmevent.h:72
bool autoRepeat
Definition qwasmevent.h:74
KeyEvent(EventType type, emscripten::val webEvent, QWasmDeadKeySupport *deadKeySupport)
MouseEvent(EventType type, emscripten::val webEvent)
PointerType pointerType
Definition qwasmevent.h:128
PointerEvent(EventType type, emscripten::val webEvent)
DeltaMode deltaMode
Definition qwasmevent.h:157
WheelEvent(EventType type, emscripten::val webEvent)
bool webkitDirectionInvertedFromDevice
Definition qwasmevent.h:158