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
104bool Event::isTargetedForElement(emscripten::val element) const
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 topTarget = webEvent.call<emscripten::val>("composedPath")[0];
111 return element == topTarget;
112}
113
114KeyEvent::KeyEvent(EventType type, emscripten::val event) : Event(type, event)
115{
116 const auto code = event["code"].as<std::string>();
117 const auto webKey = event["key"].as<std::string>();
118 deadKey = isDeadKeyEvent(webKey.c_str());
119 autoRepeat = event["repeat"].as<bool>();
120 modifiers = getKeyboardModifiers(event);
121 key = webKeyToQtKey(code, webKey, deadKey, modifiers);
122 isComposing = event["isComposing"].as<bool>();
123 keyCode = event["keyCode"].as<int>();
124
125 text = QString::fromUtf8(webKey);
126
127 // Alt + keypad number -> insert utf-8 character
128 // The individual numbers shall not be inserted but
129 // on some platforms they are if numlock is
130 // activated
131 if ((modifiers & Qt::AltModifier) && (modifiers & Qt::KeypadModifier))
132 text.clear();
133
134 if (text.size() > 1)
135 text.clear();
136
137 if (key == Qt::Key_Tab)
138 text = "\t";
139}
140
141MouseEvent::MouseEvent(EventType type, emscripten::val event) : Event(type, event)
142{
143 mouseButton = MouseEvent::buttonFromWeb(event["button"].as<int>());
144 mouseButtons = MouseEvent::buttonsFromWeb(event["buttons"].as<unsigned short>());
145 // The current button state (event.buttons) may be out of sync for some PointerDown
146 // events where the "down" state is very brief, for example taps on Apple trackpads.
147 // Qt expects that the current button state is in sync with the event, so we sync
148 // it up here.
149 if (type == EventType::PointerDown)
150 mouseButtons |= mouseButton;
151 localPoint = QPointF(event["offsetX"].as<qreal>(), event["offsetY"].as<qreal>());
152 pointInPage = QPointF(event["pageX"].as<qreal>(), event["pageY"].as<qreal>());
153 pointInViewport = QPointF(event["clientX"].as<qreal>(), event["clientY"].as<qreal>());
154 modifiers = getKeyboardModifiers(event);
155}
156
157PointerEvent::PointerEvent(EventType type, emscripten::val event) : MouseEvent(type, event)
158{
159 pointerId = event["pointerId"].as<int>();
160 pointerType = ([type = event["pointerType"].as<std::string>()]() {
161 if (type == "mouse")
162 return PointerType::Mouse;
163 if (type == "touch")
164 return PointerType::Touch;
165 if (type == "pen")
166 return PointerType::Pen;
167 return PointerType::Other;
168 })();
169 width = event["width"].as<qreal>();
170 height = event["height"].as<qreal>();
171 pressure = event["pressure"].as<qreal>();
172 tiltX = event["tiltX"].as<qreal>();
173 tiltY = event["tiltY"].as<qreal>();
174 tangentialPressure = event["tangentialPressure"].as<qreal>();
175 twist = event["twist"].as<qreal>();
176 isPrimary = event["isPrimary"].as<bool>();
177}
178
179DragEvent::DragEvent(EventType type, emscripten::val event, QWindow *window)
180 : MouseEvent(type, event), dataTransfer(event["dataTransfer"]), targetWindow(window)
181{
182 dropAction = ([event]() {
183 const std::string effect = event["dataTransfer"]["dropEffect"].as<std::string>();
184
185 if (effect == "copy")
186 return Qt::CopyAction;
187 else if (effect == "move")
188 return Qt::MoveAction;
189 else if (effect == "link")
190 return Qt::LinkAction;
191 return Qt::IgnoreAction;
192 })();
193}
194
196{
197 Q_ASSERT_X(type == EventType::DragStart, Q_FUNC_INFO, "Only supported for DragStart");
198 webEvent.call<void>("preventDefault");
199}
200
202{
203 Q_ASSERT_X(type == EventType::DragOver, Q_FUNC_INFO, "Only supported for DragOver");
204 webEvent.call<void>("preventDefault");
205}
206
208{
209 Q_ASSERT_X(type == EventType::Drop, Q_FUNC_INFO, "Only supported for Drop");
210 webEvent.call<void>("preventDefault");
211}
212
213WheelEvent::WheelEvent(EventType type, emscripten::val event) : MouseEvent(type, event)
214{
215 deltaMode = ([event]() {
216 const int deltaMode = event["deltaMode"].as<int>();
217 const auto jsWheelEventType = emscripten::val::global("WheelEvent");
218 if (deltaMode == jsWheelEventType["DOM_DELTA_PIXEL"].as<int>())
219 return DeltaMode::Pixel;
220 else if (deltaMode == jsWheelEventType["DOM_DELTA_LINE"].as<int>())
221 return DeltaMode::Line;
222 return DeltaMode::Page;
223 })();
224
225 delta = QPointF(event["deltaX"].as<qreal>(), event["deltaY"].as<qreal>());
226
227 webkitDirectionInvertedFromDevice = event["webkitDirectionInvertedFromDevice"].as<bool>();
228}
229
230QT_END_NAMESPACE
Combined button and popup list for selecting options.
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:52
PointerType
Definition qwasmevent.h:40
EventType
Definition qwasmevent.h:22
DragEvent(EventType type, emscripten::val webEvent, QWindow *targetQWindow)
void acceptDrop()
void acceptDragOver()
void cancelDragStart()
QWindow * targetWindow
Definition qwasmevent.h:151
bool isTargetedForElement(emscripten::val element) const
Event(EventType type, emscripten::val webEvent)
EventType type
Definition qwasmevent.h:61
bool deadKey
Definition qwasmevent.h:71
bool autoRepeat
Definition qwasmevent.h:73
int keyCode
Definition qwasmevent.h:75
KeyEvent(EventType type, emscripten::val webEvent)
bool isComposing
Definition qwasmevent.h:74
MouseEvent(EventType type, emscripten::val webEvent)
PointerType pointerType
Definition qwasmevent.h:129
PointerEvent(EventType type, emscripten::val webEvent)
DeltaMode deltaMode
Definition qwasmevent.h:158
WheelEvent(EventType type, emscripten::val webEvent)
bool webkitDirectionInvertedFromDevice
Definition qwasmevent.h:159