diff options
author | Mikolaj Boc <mikolaj.boc@qt.io> | 2023-01-24 10:18:34 +0100 |
---|---|---|
committer | Mikolaj Boc <mikolaj.boc@qt.io> | 2023-02-07 18:59:59 +0100 |
commit | d141d6894960dba7c62d67400a31a49a4abcdb08 (patch) | |
tree | 8711444671b98ec1f0d889dc9cfc9c0ef013efc3 /src/plugins/platforms/wasm | |
parent | 62be4ab5be41b09eea16e5675ed4a74c795e58d9 (diff) |
Transfer the key handling logic to QWasmWindow
Also, use the embind approach as the rest of the events do, and
introduce a KeyEvent class which simplifies and streamlines event
support.
The event translator has been given a more specific function of
just handling the dead keys. Rest of the translation functionality
is coded directly in KeyEvent for more encapsulation.
Change-Id: I11b0262fc42fe920206ecc6de0d434b9d9ab9998
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
Diffstat (limited to 'src/plugins/platforms/wasm')
-rw-r--r-- | src/plugins/platforms/wasm/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmclipboard.cpp | 7 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmclipboard.h | 7 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmcompositor.cpp | 47 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmcompositor.h | 8 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmevent.cpp | 76 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmevent.h | 40 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmeventtranslator.cpp | 339 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmeventtranslator.h | 46 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasminputcontext.cpp | 2 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmintegration.cpp | 7 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmkeytranslator.cpp | 249 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmkeytranslator.h | 34 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmscreen.cpp | 14 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmscreen.h | 6 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmwindow.cpp | 45 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmwindow.h | 10 |
17 files changed, 463 insertions, 476 deletions
diff --git a/src/plugins/platforms/wasm/CMakeLists.txt b/src/plugins/platforms/wasm/CMakeLists.txt index b9be181e55..6482de310c 100644 --- a/src/plugins/platforms/wasm/CMakeLists.txt +++ b/src/plugins/platforms/wasm/CMakeLists.txt @@ -23,9 +23,9 @@ qt_internal_add_plugin(QWasmIntegrationPlugin qwasmdom.cpp qwasmdom.h qwasmevent.cpp qwasmevent.h qwasmeventdispatcher.cpp qwasmeventdispatcher.h - qwasmeventtranslator.cpp qwasmeventtranslator.h qwasmfontdatabase.cpp qwasmfontdatabase.h qwasmintegration.cpp qwasmintegration.h + qwasmkeytranslator.cpp qwasmkeytranslator.h qwasmoffscreensurface.cpp qwasmoffscreensurface.h qwasmopenglcontext.cpp qwasmopenglcontext.h qwasmplatform.cpp qwasmplatform.h diff --git a/src/plugins/platforms/wasm/qwasmclipboard.cpp b/src/plugins/platforms/wasm/qwasmclipboard.cpp index df54a47ea9..1ea730f66d 100644 --- a/src/plugins/platforms/wasm/qwasmclipboard.cpp +++ b/src/plugins/platforms/wasm/qwasmclipboard.cpp @@ -3,6 +3,7 @@ #include "qwasmclipboard.h" #include "qwasmdom.h" +#include "qwasmevent.h" #include "qwasmwindow.h" #include <private/qstdweb_p.h> @@ -129,11 +130,9 @@ void QWasmClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode) writeToClipboard(); } -QWasmClipboard::ProcessKeyboardResult -QWasmClipboard::processKeyboard(const QWasmEventTranslator::TranslatedEvent &event, - const QFlags<Qt::KeyboardModifier> &modifiers) +QWasmClipboard::ProcessKeyboardResult QWasmClipboard::processKeyboard(const KeyEvent &event) { - if (event.type != QEvent::KeyPress || !modifiers.testFlag(Qt::ControlModifier)) + if (event.type != EventType::KeyDown || !event.modifiers.testFlag(Qt::ControlModifier)) return ProcessKeyboardResult::Ignored; if (event.key != Qt::Key_C && event.key != Qt::Key_V && event.key != Qt::Key_X) diff --git a/src/plugins/platforms/wasm/qwasmclipboard.h b/src/plugins/platforms/wasm/qwasmclipboard.h index cb649e03fe..4a21252c8e 100644 --- a/src/plugins/platforms/wasm/qwasmclipboard.h +++ b/src/plugins/platforms/wasm/qwasmclipboard.h @@ -12,10 +12,10 @@ #include <emscripten/bind.h> #include <emscripten/val.h> -#include "qwasmeventtranslator.h" - QT_BEGIN_NAMESPACE +struct KeyEvent; + class QWasmClipboard : public QObject, public QPlatformClipboard { public: @@ -34,8 +34,7 @@ public: bool supportsMode(QClipboard::Mode mode) const override; bool ownsMode(QClipboard::Mode mode) const override; - ProcessKeyboardResult processKeyboard(const QWasmEventTranslator::TranslatedEvent &event, - const QFlags<Qt::KeyboardModifier> &modifiers); + ProcessKeyboardResult processKeyboard(const KeyEvent &event); static void installEventHandlers(const emscripten::val &target); bool hasClipboardApi(); diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp index a66c5f5beb..e020d50728 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -3,7 +3,6 @@ #include "qwasmcompositor.h" #include "qwasmwindow.h" -#include "qwasmeventtranslator.h" #include "qwasmeventdispatcher.h" #include "qwasmclipboard.h" #include "qwasmevent.h" @@ -30,9 +29,7 @@ using namespace emscripten; Q_GUI_EXPORT int qt_defaultDpiX(); QWasmCompositor::QWasmCompositor(QWasmScreen *screen) - : QObject(screen), - m_windowStack(std::bind(&QWasmCompositor::onTopWindowChanged, this)), - m_eventTranslator(std::make_unique<QWasmEventTranslator>()) + : QObject(screen), m_windowStack(std::bind(&QWasmCompositor::onTopWindowChanged, this)) { m_touchDevice = std::make_unique<QPointingDevice>( "touchscreen", 1, QInputDevice::DeviceType::TouchScreen, @@ -61,12 +58,11 @@ void QWasmCompositor::onScreenDeleting() void QWasmCompositor::deregisterEventHandlers() { QByteArray screenElementSelector = screen()->eventTargetId().toUtf8(); - emscripten_set_keydown_callback(screenElementSelector.constData(), 0, 0, NULL); - emscripten_set_keyup_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); + emscripten_set_touchcancel_callback(screenElementSelector.constData(), 0, 0, NULL); } @@ -82,11 +78,6 @@ void QWasmCompositor::initEventHandlers() constexpr EM_BOOL UseCapture = 1; const QByteArray screenElementSelector = screen()->eventTargetId().toUtf8(); - emscripten_set_keydown_callback(screenElementSelector.constData(), (void *)this, UseCapture, - &keyboard_cb); - emscripten_set_keyup_callback(screenElementSelector.constData(), (void *)this, UseCapture, - &keyboard_cb); - emscripten_set_touchstart_callback(screenElementSelector.constData(), (void *)this, UseCapture, &touchCallback); emscripten_set_touchend_callback(screenElementSelector.constData(), (void *)this, UseCapture, @@ -283,46 +274,12 @@ QWasmScreen *QWasmCompositor::screen() return static_cast<QWasmScreen *>(parent()); } -int QWasmCompositor::keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData) -{ - QWasmCompositor *wasmCompositor = reinterpret_cast<QWasmCompositor *>(userData); - return static_cast<int>(wasmCompositor->processKeyboard(eventType, keyEvent)); -} - int QWasmCompositor::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) { auto compositor = reinterpret_cast<QWasmCompositor*>(userData); return static_cast<int>(compositor->processTouch(eventType, touchEvent)); } -bool QWasmCompositor::processKeyboard(int eventType, const EmscriptenKeyboardEvent *emKeyEvent) -{ - constexpr bool ProceedToNativeEvent = false; - Q_ASSERT(eventType == EMSCRIPTEN_EVENT_KEYDOWN || eventType == EMSCRIPTEN_EVENT_KEYUP); - - auto translatedEvent = m_eventTranslator->translateKeyEvent(eventType, emKeyEvent); - - const QFlags<Qt::KeyboardModifier> modifiers = KeyboardModifier::getForEvent(*emKeyEvent); - - const auto clipboardResult = QWasmIntegration::get()->getWasmClipboard()->processKeyboard( - translatedEvent, modifiers); - - using ProcessKeyboardResult = QWasmClipboard::ProcessKeyboardResult; - if (clipboardResult == ProcessKeyboardResult::NativeClipboardEventNeeded) - return ProceedToNativeEvent; - - if (translatedEvent.text.isEmpty()) - translatedEvent.text = QString(emKeyEvent->key); - if (translatedEvent.text.size() > 1) - translatedEvent.text.clear(); - const auto result = - QWindowSystemInterface::handleKeyEvent( - 0, translatedEvent.type, translatedEvent.key, modifiers, translatedEvent.text); - return clipboardResult == ProcessKeyboardResult::NativeClipboardEventAndCopiedDataNeeded - ? ProceedToNativeEvent - : result; -} - bool QWasmCompositor::processTouch(int eventType, const EmscriptenTouchEvent *touchEvent) { QList<QWindowSystemInterface::TouchPoint> touchPointList; diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h index 55f93209f3..95e5de818d 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.h +++ b/src/plugins/platforms/wasm/qwasmcompositor.h @@ -26,7 +26,6 @@ class QWasmWindow; class QWasmScreen; class QOpenGLContext; class QOpenGLTexture; -class QWasmEventTranslator; class QWasmCompositor final : public QObject { @@ -69,11 +68,8 @@ private: void deliverUpdateRequests(); void deliverUpdateRequest(QWasmWindow *window, UpdateRequestDeliveryType updateType); - static int keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData); - static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData); - bool processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent); bool processTouch(int eventType, const EmscriptenTouchEvent *touchEvent); void updateEnabledState(); @@ -88,9 +84,7 @@ private: std::unique_ptr<QPointingDevice> m_touchDevice; - QMap <int, QPointF> m_pressedTouchIds; - - std::unique_ptr<QWasmEventTranslator> m_eventTranslator; + QMap<int, QPointF> m_pressedTouchIds; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmevent.cpp b/src/plugins/platforms/wasm/qwasmevent.cpp index eb2c8c145a..8caea84053 100644 --- a/src/plugins/platforms/wasm/qwasmevent.cpp +++ b/src/plugins/platforms/wasm/qwasmevent.cpp @@ -3,8 +3,39 @@ #include "qwasmevent.h" +#include "qwasmkeytranslator.h" + +#include <QtCore/private/qmakearray_p.h> +#include <QtCore/private/qstringiterator_p.h> + QT_BEGIN_NAMESPACE +namespace { +constexpr std::string_view WebDeadKeyValue = "Dead"; + +bool isDeadKeyEvent(const char *key) +{ + return qstrncmp(key, WebDeadKeyValue.data(), WebDeadKeyValue.size()) == 0; +} + +Qt::Key webKeyToQtKey(const std::string &code, const std::string &key, bool isDeadKey) +{ + if (isDeadKey) { + if (auto mapping = QWasmKeyTranslator::mapWebKeyTextToQtKey(code.c_str())) + return *mapping; + } + if (auto mapping = QWasmKeyTranslator::mapWebKeyTextToQtKey(key.c_str())) + return *mapping; + if (isDeadKey) + return Qt::Key_unknown; + + // cast to unicode key + QString str = QString::fromUtf8(key.c_str()).toUpper(); + QStringIterator i(str); + return static_cast<Qt::Key>(i.next(0)); +} +} // namespace + namespace KeyboardModifier { template <> @@ -28,6 +59,51 @@ Event &Event::operator=(const Event &other) = default; Event &Event::operator=(Event &&other) = default; +KeyEvent::KeyEvent(EventType type, emscripten::val event) : Event(type, event["target"]) +{ + const auto code = event["code"].as<std::string>(); + const auto webKey = event["key"].as<std::string>(); + deadKey = isDeadKeyEvent(webKey.c_str()); + + key = webKeyToQtKey(code, webKey, deadKey); + + modifiers = KeyboardModifier::getForEvent(event); + text = QString::fromUtf8(webKey); + if (text.size() > 1) + text.clear(); +} + +KeyEvent::~KeyEvent() = default; + +KeyEvent::KeyEvent(const KeyEvent &other) = default; + +KeyEvent::KeyEvent(KeyEvent &&other) = default; + +KeyEvent &KeyEvent::operator=(const KeyEvent &other) = default; + +KeyEvent &KeyEvent::operator=(KeyEvent &&other) = default; + +std::optional<KeyEvent> KeyEvent::fromWebWithDeadKeyTranslation(emscripten::val event, + QWasmDeadKeySupport *deadKeySupport) +{ + const auto eventType = ([&event]() -> std::optional<EventType> { + const auto eventTypeString = event["type"].as<std::string>(); + + if (eventTypeString == "keydown") + return EventType::KeyDown; + else if (eventTypeString == "keyup") + return EventType::KeyUp; + return std::nullopt; + })(); + if (!eventType) + return std::nullopt; + + auto result = KeyEvent(*eventType, event); + deadKeySupport->applyDeadKeyTranslations(&result); + + return result; +} + MouseEvent::MouseEvent(EventType type, emscripten::val event) : Event(type, event["target"]) { mouseButton = MouseEvent::buttonFromWeb(event["button"].as<int>()); diff --git a/src/plugins/platforms/wasm/qwasmevent.h b/src/plugins/platforms/wasm/qwasmevent.h index e8aea9072e..73bb28d267 100644 --- a/src/plugins/platforms/wasm/qwasmevent.h +++ b/src/plugins/platforms/wasm/qwasmevent.h @@ -17,8 +17,12 @@ QT_BEGIN_NAMESPACE +class QWasmDeadKeySupport; + enum class EventType { Drop, + KeyDown, + KeyUp, PointerDown, PointerMove, PointerUp, @@ -113,26 +117,37 @@ QFlags<Qt::KeyboardModifier> getForEvent<EmscriptenKeyboardEvent>( struct Event { - EventType type; - emscripten::val target = emscripten::val::undefined(); - Event(EventType type, emscripten::val target); ~Event(); Event(const Event &other); Event(Event &&other); Event &operator=(const Event &other); Event &operator=(Event &&other); + + EventType type; + emscripten::val target = emscripten::val::undefined(); }; -struct MouseEvent : public Event +struct KeyEvent : public Event { - QPoint localPoint; - QPoint pointInPage; - QPoint pointInViewport; - Qt::MouseButton mouseButton; - Qt::MouseButtons mouseButtons; + static std::optional<KeyEvent> + fromWebWithDeadKeyTranslation(emscripten::val webEvent, QWasmDeadKeySupport *deadKeySupport); + + KeyEvent(EventType type, emscripten::val webEvent); + ~KeyEvent(); + KeyEvent(const KeyEvent &other); + KeyEvent(KeyEvent &&other); + KeyEvent &operator=(const KeyEvent &other); + KeyEvent &operator=(KeyEvent &&other); + + Qt::Key key; QFlags<Qt::KeyboardModifier> modifiers; + bool deadKey; + QString text; +}; +struct MouseEvent : public Event +{ MouseEvent(EventType type, emscripten::val webEvent); ~MouseEvent(); MouseEvent(const MouseEvent &other); @@ -174,6 +189,13 @@ struct MouseEvent : public Event return QEvent::None; } } + + QPoint localPoint; + QPoint pointInPage; + QPoint pointInViewport; + Qt::MouseButton mouseButton; + Qt::MouseButtons mouseButtons; + QFlags<Qt::KeyboardModifier> modifiers; }; struct PointerEvent : public MouseEvent diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp deleted file mode 100644 index 395c9c3ee0..0000000000 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp +++ /dev/null @@ -1,339 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -#include "qwasmeventtranslator.h" -#include "qwasmeventdispatcher.h" -#include "qwasmcompositor.h" -#include "qwasmintegration.h" -#include "qwasmclipboard.h" -#include "qwasmcursor.h" -#include <QtGui/qevent.h> -#include <qpa/qwindowsysteminterface.h> -#include <QtCore/qcoreapplication.h> -#include <QtCore/qglobal.h> -#include <QtCore/qobject.h> - -#include <QtCore/qdeadlinetimer.h> -#include <private/qmakearray_p.h> -#include <QtCore/qnamespace.h> -#include <QCursor> -#include <QtCore/private/qstringiterator_p.h> - -#include <emscripten/bind.h> - -#include <iostream> - -QT_BEGIN_NAMESPACE - -using namespace emscripten; - -namespace { -constexpr std::string_view WebDeadKeyValue = "Dead"; - -struct Emkb2QtData -{ - static constexpr char StringTerminator = '\0'; - - const char *em; - unsigned int qt; - - constexpr bool operator<=(const Emkb2QtData &that) const noexcept - { - return !(strcmp(that) > 0); - } - - bool operator<(const Emkb2QtData &that) const noexcept { return ::strcmp(em, that.em) < 0; } - - constexpr bool operator==(const Emkb2QtData &that) const noexcept { return strcmp(that) == 0; } - - constexpr int strcmp(const Emkb2QtData &that, const int i = 0) const - { - return em[i] == StringTerminator && that.em[i] == StringTerminator ? 0 - : em[i] == StringTerminator ? -1 - : that.em[i] == StringTerminator ? 1 - : em[i] < that.em[i] ? -1 - : em[i] > that.em[i] ? 1 - : strcmp(that, i + 1); - } -}; - -template<unsigned int Qt, char ... EmChar> -struct Emkb2Qt -{ - static constexpr const char storage[sizeof ... (EmChar) + 1] = {EmChar..., '\0'}; - using Type = Emkb2QtData; - static constexpr Type data() noexcept { return Type{storage, Qt}; } -}; - -template<unsigned int Qt, char ... EmChar> constexpr char Emkb2Qt<Qt, EmChar...>::storage[]; - -static constexpr const auto WebToQtKeyCodeMappings = qMakeArray( - QSortedData< - Emkb2Qt< Qt::Key_Escape, 'E','s','c','a','p','e' >, - Emkb2Qt< Qt::Key_Tab, 'T','a','b' >, - Emkb2Qt< Qt::Key_Backspace, 'B','a','c','k','s','p','a','c','e' >, - Emkb2Qt< Qt::Key_Return, 'E','n','t','e','r' >, - Emkb2Qt< Qt::Key_Insert, 'I','n','s','e','r','t' >, - Emkb2Qt< Qt::Key_Delete, 'D','e','l','e','t','e' >, - Emkb2Qt< Qt::Key_Pause, 'P','a','u','s','e' >, - Emkb2Qt< Qt::Key_Pause, 'C','l','e','a','r' >, - Emkb2Qt< Qt::Key_Home, 'H','o','m','e' >, - Emkb2Qt< Qt::Key_End, 'E','n','d' >, - Emkb2Qt< Qt::Key_Left, 'A','r','r','o','w','L','e','f','t' >, - Emkb2Qt< Qt::Key_Up, 'A','r','r','o','w','U','p' >, - Emkb2Qt< Qt::Key_Right, 'A','r','r','o','w','R','i','g','h','t' >, - Emkb2Qt< Qt::Key_Down, 'A','r','r','o','w','D','o','w','n' >, - Emkb2Qt< Qt::Key_PageUp, 'P','a','g','e','U','p' >, - Emkb2Qt< Qt::Key_PageDown, 'P','a','g','e','D','o','w','n' >, - Emkb2Qt< Qt::Key_Shift, 'S','h','i','f','t' >, - Emkb2Qt< Qt::Key_Control, 'C','o','n','t','r','o','l' >, - Emkb2Qt< Qt::Key_Meta, 'M','e','t','a'>, - Emkb2Qt< Qt::Key_Meta, 'O','S'>, - Emkb2Qt< Qt::Key_Alt, 'A','l','t','L','e','f','t' >, - Emkb2Qt< Qt::Key_Alt, 'A','l','t' >, - Emkb2Qt< Qt::Key_CapsLock, 'C','a','p','s','L','o','c','k' >, - Emkb2Qt< Qt::Key_NumLock, 'N','u','m','L','o','c','k' >, - Emkb2Qt< Qt::Key_ScrollLock, 'S','c','r','o','l','l','L','o','c','k' >, - Emkb2Qt< Qt::Key_F1, 'F','1' >, - Emkb2Qt< Qt::Key_F2, 'F','2' >, - Emkb2Qt< Qt::Key_F3, 'F','3' >, - Emkb2Qt< Qt::Key_F4, 'F','4' >, - Emkb2Qt< Qt::Key_F5, 'F','5' >, - Emkb2Qt< Qt::Key_F6, 'F','6' >, - Emkb2Qt< Qt::Key_F7, 'F','7' >, - Emkb2Qt< Qt::Key_F8, 'F','8' >, - Emkb2Qt< Qt::Key_F9, 'F','9' >, - Emkb2Qt< Qt::Key_F10, 'F','1','0' >, - Emkb2Qt< Qt::Key_F11, 'F','1','1' >, - Emkb2Qt< Qt::Key_F12, 'F','1','2' >, - Emkb2Qt< Qt::Key_F13, 'F','1','3' >, - Emkb2Qt< Qt::Key_F14, 'F','1','4' >, - Emkb2Qt< Qt::Key_F15, 'F','1','5' >, - Emkb2Qt< Qt::Key_F16, 'F','1','6' >, - Emkb2Qt< Qt::Key_F17, 'F','1','7' >, - Emkb2Qt< Qt::Key_F18, 'F','1','8' >, - Emkb2Qt< Qt::Key_F19, 'F','1','9' >, - Emkb2Qt< Qt::Key_F20, 'F','2','0' >, - Emkb2Qt< Qt::Key_F21, 'F','2','1' >, - Emkb2Qt< Qt::Key_F22, 'F','2','2' >, - Emkb2Qt< Qt::Key_F23, 'F','2','3' >, - Emkb2Qt< Qt::Key_Paste, 'P','a','s','t','e' >, - Emkb2Qt< Qt::Key_AltGr, 'A','l','t','R','i','g','h','t' >, - Emkb2Qt< Qt::Key_Help, 'H','e','l','p' >, - Emkb2Qt< Qt::Key_yen, 'I','n','t','l','Y','e','n' >, - Emkb2Qt< Qt::Key_Menu, 'C','o','n','t','e','x','t','M','e','n','u' > - >::Data{} - ); - -static constexpr const auto WebToQtKeyCodeMappingsWithShift = qMakeArray( - QSortedData< - // shifted - Emkb2Qt< Qt::Key_Agrave, '\xc3','\x80' >, - Emkb2Qt< Qt::Key_Aacute, '\xc3','\x81' >, - Emkb2Qt< Qt::Key_Acircumflex, '\xc3','\x82' >, - Emkb2Qt< Qt::Key_Adiaeresis, '\xc3','\x84' >, - Emkb2Qt< Qt::Key_AE, '\xc3','\x86' >, - Emkb2Qt< Qt::Key_Atilde, '\xc3','\x83' >, - Emkb2Qt< Qt::Key_Aring, '\xc3','\x85' >, - Emkb2Qt< Qt::Key_Egrave, '\xc3','\x88' >, - Emkb2Qt< Qt::Key_Eacute, '\xc3','\x89' >, - Emkb2Qt< Qt::Key_Ecircumflex, '\xc3','\x8a' >, - Emkb2Qt< Qt::Key_Ediaeresis, '\xc3','\x8b' >, - Emkb2Qt< Qt::Key_Iacute, '\xc3','\x8d' >, - Emkb2Qt< Qt::Key_Icircumflex, '\xc3','\x8e' >, - Emkb2Qt< Qt::Key_Idiaeresis, '\xc3','\x8f' >, - Emkb2Qt< Qt::Key_Igrave, '\xc3','\x8c' >, - Emkb2Qt< Qt::Key_Ocircumflex, '\xc3','\x94' >, - Emkb2Qt< Qt::Key_Odiaeresis, '\xc3','\x96' >, - Emkb2Qt< Qt::Key_Ograve, '\xc3','\x92' >, - Emkb2Qt< Qt::Key_Oacute, '\xc3','\x93' >, - Emkb2Qt< Qt::Key_Ooblique, '\xc3','\x98' >, - Emkb2Qt< Qt::Key_Otilde, '\xc3','\x95' >, - Emkb2Qt< Qt::Key_Ucircumflex, '\xc3','\x9b' >, - Emkb2Qt< Qt::Key_Udiaeresis, '\xc3','\x9c' >, - Emkb2Qt< Qt::Key_Ugrave, '\xc3','\x99' >, - Emkb2Qt< Qt::Key_Uacute, '\xc3','\x9a' >, - Emkb2Qt< Qt::Key_Ntilde, '\xc3','\x91' >, - Emkb2Qt< Qt::Key_Ccedilla, '\xc3','\x87' >, - Emkb2Qt< Qt::Key_ydiaeresis, '\xc3','\x8f' >, - Emkb2Qt< Qt::Key_Yacute, '\xc3','\x9d' > - >::Data{} -); - -std::optional<Qt::Key> findMappingByBisection(const char *toFind) -{ - const Emkb2QtData searchKey{ toFind, 0 }; - const auto it = std::lower_bound(WebToQtKeyCodeMappings.cbegin(), WebToQtKeyCodeMappings.cend(), - searchKey); - return it != WebToQtKeyCodeMappings.cend() && searchKey == *it ? static_cast<Qt::Key>(it->qt) - : std::optional<Qt::Key>(); -} - -bool isDeadKeyEvent(const EmscriptenKeyboardEvent *emKeyEvent) -{ - return qstrncmp(emKeyEvent->key, WebDeadKeyValue.data(), WebDeadKeyValue.size()) == 0; -} - -Qt::Key translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey) -{ - const bool deadKeyEvent = isDeadKeyEvent(emscriptKey); - if (deadKeyEvent) { - if (auto mapping = findMappingByBisection(emscriptKey->code)) - return *mapping; - } - if (auto mapping = findMappingByBisection(emscriptKey->key)) - return *mapping; - if (deadKeyEvent) - return Qt::Key_unknown; - - // cast to unicode key - QString str = QString::fromUtf8(emscriptKey->key).toUpper(); - QStringIterator i(str); - return static_cast<Qt::Key>(i.next(0)); -} - -struct KeyMapping { Qt::Key from, to; }; - -constexpr KeyMapping tildeKeyTable[] = { // ~ - { Qt::Key_A, Qt::Key_Atilde }, - { Qt::Key_N, Qt::Key_Ntilde }, - { Qt::Key_O, Qt::Key_Otilde }, -}; -constexpr KeyMapping graveKeyTable[] = { // ` - { Qt::Key_A, Qt::Key_Agrave }, - { Qt::Key_E, Qt::Key_Egrave }, - { Qt::Key_I, Qt::Key_Igrave }, - { Qt::Key_O, Qt::Key_Ograve }, - { Qt::Key_U, Qt::Key_Ugrave }, -}; -constexpr KeyMapping acuteKeyTable[] = { // ' - { Qt::Key_A, Qt::Key_Aacute }, - { Qt::Key_E, Qt::Key_Eacute }, - { Qt::Key_I, Qt::Key_Iacute }, - { Qt::Key_O, Qt::Key_Oacute }, - { Qt::Key_U, Qt::Key_Uacute }, - { Qt::Key_Y, Qt::Key_Yacute }, -}; -constexpr KeyMapping diaeresisKeyTable[] = { // umlaut ¨ - { Qt::Key_A, Qt::Key_Adiaeresis }, - { Qt::Key_E, Qt::Key_Ediaeresis }, - { Qt::Key_I, Qt::Key_Idiaeresis }, - { Qt::Key_O, Qt::Key_Odiaeresis }, - { Qt::Key_U, Qt::Key_Udiaeresis }, - { Qt::Key_Y, Qt::Key_ydiaeresis }, -}; -constexpr KeyMapping circumflexKeyTable[] = { // ^ - { Qt::Key_A, Qt::Key_Acircumflex }, - { Qt::Key_E, Qt::Key_Ecircumflex }, - { Qt::Key_I, Qt::Key_Icircumflex }, - { Qt::Key_O, Qt::Key_Ocircumflex }, - { Qt::Key_U, Qt::Key_Ucircumflex }, -}; - -static Qt::Key find_impl(const KeyMapping *first, const KeyMapping *last, Qt::Key key) noexcept -{ - while (first != last) { - if (first->from == key) - return first->to; - ++first; - } - return Qt::Key_unknown; -} - -template <size_t N> -static Qt::Key find(const KeyMapping (&map)[N], Qt::Key key) noexcept -{ - return find_impl(map, map + N, key); -} - -Qt::Key translateBaseKeyUsingDeadKey(Qt::Key accentBaseKey, Qt::Key deadKey) -{ - switch (deadKey) { - case Qt::Key_QuoteLeft: { - // ` macOS: Key_Dead_Grave - return platform() == Platform::MacOS ? find(graveKeyTable, accentBaseKey) - : find(diaeresisKeyTable, accentBaseKey); - } - case Qt::Key_O: // ´ Key_Dead_Grave - return find(graveKeyTable, accentBaseKey); - case Qt::Key_E: // ´ Key_Dead_Acute - return find(acuteKeyTable, accentBaseKey); - case Qt::Key_AsciiTilde: - case Qt::Key_N: // Key_Dead_Tilde - return find(tildeKeyTable, accentBaseKey); - case Qt::Key_U: // ¨ Key_Dead_Diaeresis - return find(diaeresisKeyTable, accentBaseKey); - case Qt::Key_I: // macOS Key_Dead_Circumflex - case Qt::Key_6: // linux - case Qt::Key_Apostrophe: // linux - return find(circumflexKeyTable, accentBaseKey); - default: - return Qt::Key_unknown; - }; -} - -template<class T> -std::optional<QString> findKeyTextByKeyId(const T &mappingArray, Qt::Key qtKey) -{ - const auto it = std::find_if(mappingArray.cbegin(), mappingArray.cend(), - [qtKey](const Emkb2QtData &data) { return data.qt == qtKey; }); - return it != mappingArray.cend() ? it->em : std::optional<QString>(); -} -} // namespace - -QWasmEventTranslator::QWasmEventTranslator() = default; - -QWasmEventTranslator::~QWasmEventTranslator() = default; - -QWasmEventTranslator::TranslatedEvent -QWasmEventTranslator::translateKeyEvent(int emEventType, const EmscriptenKeyboardEvent *keyEvent) -{ - TranslatedEvent ret; - switch (emEventType) { - case EMSCRIPTEN_EVENT_KEYDOWN: - ret.type = QEvent::KeyPress; - break; - case EMSCRIPTEN_EVENT_KEYUP: - ret.type = QEvent::KeyRelease; - break; - default: - // Should not be reached - do not call with this event type. - Q_ASSERT(false); - break; - }; - - ret.key = translateEmscriptKey(keyEvent); - - if (isDeadKeyEvent(keyEvent) || ret.key == Qt::Key_AltGr) { - if (keyEvent->shiftKey && ret.key == Qt::Key_QuoteLeft) - ret.key = Qt::Key_AsciiTilde; - m_emDeadKey = ret.key; - } else if (m_emDeadKey != Qt::Key_unknown - && (m_keyModifiedByDeadKeyOnPress == Qt::Key_unknown - || ret.key == m_keyModifiedByDeadKeyOnPress)) { - const Qt::Key baseKey = ret.key; - const Qt::Key translatedKey = translateBaseKeyUsingDeadKey(baseKey, m_emDeadKey); - if (translatedKey != Qt::Key_unknown) - ret.key = translatedKey; - - if (auto text = keyEvent->shiftKey - ? findKeyTextByKeyId(WebToQtKeyCodeMappingsWithShift, ret.key) - : findKeyTextByKeyId(WebToQtKeyCodeMappings, ret.key)) { - if (ret.type == QEvent::KeyPress) { - Q_ASSERT(m_keyModifiedByDeadKeyOnPress == Qt::Key_unknown); - m_keyModifiedByDeadKeyOnPress = baseKey; - } else { - Q_ASSERT(ret.type == QEvent::KeyRelease); - Q_ASSERT(m_keyModifiedByDeadKeyOnPress == baseKey); - m_keyModifiedByDeadKeyOnPress = Qt::Key_unknown; - m_emDeadKey = Qt::Key_unknown; - } - ret.text = *text; - return ret; - } - } - ret.text = QString::fromUtf8(keyEvent->key); - return ret; -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.h b/src/plugins/platforms/wasm/qwasmeventtranslator.h deleted file mode 100644 index 23299c294f..0000000000 --- a/src/plugins/platforms/wasm/qwasmeventtranslator.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -#ifndef QWASMEVENTTRANSLATOR_H -#define QWASMEVENTTRANSLATOR_H - -#include <QtCore/qobject.h> -#include <QtCore/qrect.h> -#include <QtCore/qpoint.h> -#include <emscripten/html5.h> -#include "qwasmwindow.h" -#include <QtGui/qinputdevice.h> -#include <QHash> -#include <QCursor> -#include "qwasmevent.h" -#include "qwasmplatform.h" - -QT_BEGIN_NAMESPACE - -class QWindow; - -class QWasmEventTranslator : public QObject -{ - Q_OBJECT - -public: - struct TranslatedEvent - { - QEvent::Type type; - Qt::Key key; - QString text; - }; - explicit QWasmEventTranslator(); - ~QWasmEventTranslator(); - - TranslatedEvent translateKeyEvent(int emEventType, const EmscriptenKeyboardEvent *keyEvent); - -private: - static quint64 getTimestamp(); - - Qt::Key m_emDeadKey = Qt::Key_unknown; - Qt::Key m_keyModifiedByDeadKeyOnPress = Qt::Key_unknown; -}; - -QT_END_NAMESPACE -#endif // QWASMEVENTTRANSLATOR_H diff --git a/src/plugins/platforms/wasm/qwasminputcontext.cpp b/src/plugins/platforms/wasm/qwasminputcontext.cpp index 1c004b226f..637130c070 100644 --- a/src/plugins/platforms/wasm/qwasminputcontext.cpp +++ b/src/plugins/platforms/wasm/qwasminputcontext.cpp @@ -5,9 +5,9 @@ #include "qwasminputcontext.h" #include "qwasmintegration.h" +#include "qwasmplatform.h" #include <QRectF> #include <qpa/qplatforminputcontext.h> -#include "qwasmeventtranslator.h" #include "qwasmscreen.h" #include <qguiapplication.h> #include <qwindow.h> diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index 1487fb3f88..a88de18f2a 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qwasmintegration.h" -#include "qwasmeventtranslator.h" #include "qwasmeventdispatcher.h" #include "qwasmcompositor.h" #include "qwasmopenglcontext.h" @@ -164,8 +163,10 @@ bool QWasmIntegration::hasCapability(QPlatformIntegration::Capability cap) const QPlatformWindow *QWasmIntegration::createPlatformWindow(QWindow *window) const { - QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor(); - return new QWasmWindow(window, compositor, m_backingStores.value(window)); + auto *wasmScreen = QWasmScreen::get(window->screen()); + QWasmCompositor *compositor = wasmScreen->compositor(); + return new QWasmWindow(window, wasmScreen->deadKeySupport(), compositor, + m_backingStores.value(window)); } QPlatformBackingStore *QWasmIntegration::createPlatformBackingStore(QWindow *window) const diff --git a/src/plugins/platforms/wasm/qwasmkeytranslator.cpp b/src/plugins/platforms/wasm/qwasmkeytranslator.cpp new file mode 100644 index 0000000000..0052495b89 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmkeytranslator.cpp @@ -0,0 +1,249 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "qwasmkeytranslator.h" +#include "qwasmevent.h" + +#include <QtCore/private/qmakearray_p.h> +#include <QtCore/qglobal.h> +#include <QtCore/qobject.h> + +#include <algorithm> + +QT_BEGIN_NAMESPACE + +namespace { +struct WebKb2QtData +{ + static constexpr char StringTerminator = '\0'; + + const char *web; + unsigned int qt; + + constexpr bool operator<=(const WebKb2QtData &that) const noexcept + { + return !(strcmp(that) > 0); + } + + bool operator<(const WebKb2QtData &that) const noexcept { return ::strcmp(web, that.web) < 0; } + + constexpr bool operator==(const WebKb2QtData &that) const noexcept { return strcmp(that) == 0; } + + constexpr int strcmp(const WebKb2QtData &that, const int i = 0) const + { + return web[i] == StringTerminator && that.web[i] == StringTerminator ? 0 + : web[i] == StringTerminator ? -1 + : that.web[i] == StringTerminator ? 1 + : web[i] < that.web[i] ? -1 + : web[i] > that.web[i] ? 1 + : strcmp(that, i + 1); + } +}; + +template<unsigned int Qt, char... WebChar> +struct Web2Qt +{ + static constexpr const char storage[sizeof...(WebChar) + 1] = { WebChar..., '\0' }; + using Type = WebKb2QtData; + static constexpr Type data() noexcept { return Type{ storage, Qt }; } +}; + +template<unsigned int Qt, char... WebChar> +constexpr char Web2Qt<Qt, WebChar...>::storage[]; + +static constexpr const auto WebToQtKeyCodeMappings = qMakeArray( + QSortedData<Web2Qt<Qt::Key_Escape, 'E', 's', 'c', 'a', 'p', 'e'>, + Web2Qt<Qt::Key_Tab, 'T', 'a', 'b'>, + Web2Qt<Qt::Key_Backspace, 'B', 'a', 'c', 'k', 's', 'p', 'a', 'c', 'e'>, + Web2Qt<Qt::Key_Return, 'E', 'n', 't', 'e', 'r'>, + Web2Qt<Qt::Key_Insert, 'I', 'n', 's', 'e', 'r', 't'>, + Web2Qt<Qt::Key_Delete, 'D', 'e', 'l', 'e', 't', 'e'>, + Web2Qt<Qt::Key_Pause, 'P', 'a', 'u', 's', 'e'>, + Web2Qt<Qt::Key_Pause, 'C', 'l', 'e', 'a', 'r'>, + Web2Qt<Qt::Key_Home, 'H', 'o', 'm', 'e'>, Web2Qt<Qt::Key_End, 'E', 'n', 'd'>, + Web2Qt<Qt::Key_Left, 'A', 'r', 'r', 'o', 'w', 'L', 'e', 'f', 't'>, + Web2Qt<Qt::Key_Up, 'A', 'r', 'r', 'o', 'w', 'U', 'p'>, + Web2Qt<Qt::Key_Right, 'A', 'r', 'r', 'o', 'w', 'R', 'i', 'g', 'h', 't'>, + Web2Qt<Qt::Key_Down, 'A', 'r', 'r', 'o', 'w', 'D', 'o', 'w', 'n'>, + Web2Qt<Qt::Key_PageUp, 'P', 'a', 'g', 'e', 'U', 'p'>, + Web2Qt<Qt::Key_PageDown, 'P', 'a', 'g', 'e', 'D', 'o', 'w', 'n'>, + Web2Qt<Qt::Key_Shift, 'S', 'h', 'i', 'f', 't'>, + Web2Qt<Qt::Key_Control, 'C', 'o', 'n', 't', 'r', 'o', 'l'>, + Web2Qt<Qt::Key_Meta, 'M', 'e', 't', 'a'>, Web2Qt<Qt::Key_Meta, 'O', 'S'>, + Web2Qt<Qt::Key_Alt, 'A', 'l', 't', 'L', 'e', 'f', 't'>, + Web2Qt<Qt::Key_Alt, 'A', 'l', 't'>, + Web2Qt<Qt::Key_CapsLock, 'C', 'a', 'p', 's', 'L', 'o', 'c', 'k'>, + Web2Qt<Qt::Key_NumLock, 'N', 'u', 'm', 'L', 'o', 'c', 'k'>, + Web2Qt<Qt::Key_ScrollLock, 'S', 'c', 'r', 'o', 'l', 'l', 'L', 'o', 'c', 'k'>, + Web2Qt<Qt::Key_F1, 'F', '1'>, Web2Qt<Qt::Key_F2, 'F', '2'>, + Web2Qt<Qt::Key_F3, 'F', '3'>, Web2Qt<Qt::Key_F4, 'F', '4'>, + Web2Qt<Qt::Key_F5, 'F', '5'>, Web2Qt<Qt::Key_F6, 'F', '6'>, + Web2Qt<Qt::Key_F7, 'F', '7'>, Web2Qt<Qt::Key_F8, 'F', '8'>, + Web2Qt<Qt::Key_F9, 'F', '9'>, Web2Qt<Qt::Key_F10, 'F', '1', '0'>, + Web2Qt<Qt::Key_F11, 'F', '1', '1'>, Web2Qt<Qt::Key_F12, 'F', '1', '2'>, + Web2Qt<Qt::Key_F13, 'F', '1', '3'>, Web2Qt<Qt::Key_F14, 'F', '1', '4'>, + Web2Qt<Qt::Key_F15, 'F', '1', '5'>, Web2Qt<Qt::Key_F16, 'F', '1', '6'>, + Web2Qt<Qt::Key_F17, 'F', '1', '7'>, Web2Qt<Qt::Key_F18, 'F', '1', '8'>, + Web2Qt<Qt::Key_F19, 'F', '1', '9'>, Web2Qt<Qt::Key_F20, 'F', '2', '0'>, + Web2Qt<Qt::Key_F21, 'F', '2', '1'>, Web2Qt<Qt::Key_F22, 'F', '2', '2'>, + Web2Qt<Qt::Key_F23, 'F', '2', '3'>, + Web2Qt<Qt::Key_Paste, 'P', 'a', 's', 't', 'e'>, + Web2Qt<Qt::Key_AltGr, 'A', 'l', 't', 'R', 'i', 'g', 'h', 't'>, + Web2Qt<Qt::Key_Help, 'H', 'e', 'l', 'p'>, + Web2Qt<Qt::Key_yen, 'I', 'n', 't', 'l', 'Y', 'e', 'n'>, + Web2Qt<Qt::Key_Menu, 'C', 'o', 'n', 't', 'e', 'x', 't', 'M', 'e', 'n', + 'u'>>::Data{}); + +static constexpr const auto WebToQtKeyCodeMappingsWithShift = qMakeArray( + QSortedData< + // shifted + Web2Qt<Qt::Key_Agrave, '\xc3', '\x80'>, Web2Qt<Qt::Key_Aacute, '\xc3', '\x81'>, + Web2Qt<Qt::Key_Acircumflex, '\xc3', '\x82'>, + Web2Qt<Qt::Key_Adiaeresis, '\xc3', '\x84'>, Web2Qt<Qt::Key_AE, '\xc3', '\x86'>, + Web2Qt<Qt::Key_Atilde, '\xc3', '\x83'>, Web2Qt<Qt::Key_Aring, '\xc3', '\x85'>, + Web2Qt<Qt::Key_Egrave, '\xc3', '\x88'>, Web2Qt<Qt::Key_Eacute, '\xc3', '\x89'>, + Web2Qt<Qt::Key_Ecircumflex, '\xc3', '\x8a'>, + Web2Qt<Qt::Key_Ediaeresis, '\xc3', '\x8b'>, Web2Qt<Qt::Key_Iacute, '\xc3', '\x8d'>, + Web2Qt<Qt::Key_Icircumflex, '\xc3', '\x8e'>, + Web2Qt<Qt::Key_Idiaeresis, '\xc3', '\x8f'>, Web2Qt<Qt::Key_Igrave, '\xc3', '\x8c'>, + Web2Qt<Qt::Key_Ocircumflex, '\xc3', '\x94'>, + Web2Qt<Qt::Key_Odiaeresis, '\xc3', '\x96'>, Web2Qt<Qt::Key_Ograve, '\xc3', '\x92'>, + Web2Qt<Qt::Key_Oacute, '\xc3', '\x93'>, Web2Qt<Qt::Key_Ooblique, '\xc3', '\x98'>, + Web2Qt<Qt::Key_Otilde, '\xc3', '\x95'>, Web2Qt<Qt::Key_Ucircumflex, '\xc3', '\x9b'>, + Web2Qt<Qt::Key_Udiaeresis, '\xc3', '\x9c'>, Web2Qt<Qt::Key_Ugrave, '\xc3', '\x99'>, + Web2Qt<Qt::Key_Uacute, '\xc3', '\x9a'>, Web2Qt<Qt::Key_Ntilde, '\xc3', '\x91'>, + Web2Qt<Qt::Key_Ccedilla, '\xc3', '\x87'>, + Web2Qt<Qt::Key_ydiaeresis, '\xc3', '\x8f'>, + Web2Qt<Qt::Key_Yacute, '\xc3', '\x9d'>>::Data{}); + +struct KeyMapping +{ + Qt::Key from, to; +}; + +constexpr KeyMapping tildeKeyTable[] = { + // ~ + { Qt::Key_A, Qt::Key_Atilde }, + { Qt::Key_N, Qt::Key_Ntilde }, + { Qt::Key_O, Qt::Key_Otilde }, +}; +constexpr KeyMapping graveKeyTable[] = { + // ` + { Qt::Key_A, Qt::Key_Agrave }, { Qt::Key_E, Qt::Key_Egrave }, { Qt::Key_I, Qt::Key_Igrave }, + { Qt::Key_O, Qt::Key_Ograve }, { Qt::Key_U, Qt::Key_Ugrave }, +}; +constexpr KeyMapping acuteKeyTable[] = { + // ' + { Qt::Key_A, Qt::Key_Aacute }, { Qt::Key_E, Qt::Key_Eacute }, { Qt::Key_I, Qt::Key_Iacute }, + { Qt::Key_O, Qt::Key_Oacute }, { Qt::Key_U, Qt::Key_Uacute }, { Qt::Key_Y, Qt::Key_Yacute }, +}; +constexpr KeyMapping diaeresisKeyTable[] = { + // umlaut ¨ + { Qt::Key_A, Qt::Key_Adiaeresis }, { Qt::Key_E, Qt::Key_Ediaeresis }, + { Qt::Key_I, Qt::Key_Idiaeresis }, { Qt::Key_O, Qt::Key_Odiaeresis }, + { Qt::Key_U, Qt::Key_Udiaeresis }, { Qt::Key_Y, Qt::Key_ydiaeresis }, +}; +constexpr KeyMapping circumflexKeyTable[] = { + // ^ + { Qt::Key_A, Qt::Key_Acircumflex }, { Qt::Key_E, Qt::Key_Ecircumflex }, + { Qt::Key_I, Qt::Key_Icircumflex }, { Qt::Key_O, Qt::Key_Ocircumflex }, + { Qt::Key_U, Qt::Key_Ucircumflex }, +}; + +static Qt::Key find_impl(const KeyMapping *first, const KeyMapping *last, Qt::Key key) noexcept +{ + while (first != last) { + if (first->from == key) + return first->to; + ++first; + } + return Qt::Key_unknown; +} + +template<size_t N> +static Qt::Key find(const KeyMapping (&map)[N], Qt::Key key) noexcept +{ + return find_impl(map, map + N, key); +} + +Qt::Key translateBaseKeyUsingDeadKey(Qt::Key accentBaseKey, Qt::Key deadKey) +{ + switch (deadKey) { + case Qt::Key_QuoteLeft: { + // ` macOS: Key_Dead_Grave + return platform() == Platform::MacOS ? find(graveKeyTable, accentBaseKey) + : find(diaeresisKeyTable, accentBaseKey); + } + case Qt::Key_O: // ´ Key_Dead_Grave + return find(graveKeyTable, accentBaseKey); + case Qt::Key_E: // ´ Key_Dead_Acute + return find(acuteKeyTable, accentBaseKey); + case Qt::Key_AsciiTilde: + case Qt::Key_N: // Key_Dead_Tilde + return find(tildeKeyTable, accentBaseKey); + case Qt::Key_U: // ¨ Key_Dead_Diaeresis + return find(diaeresisKeyTable, accentBaseKey); + case Qt::Key_I: // macOS Key_Dead_Circumflex + case Qt::Key_6: // linux + case Qt::Key_Apostrophe: // linux + return find(circumflexKeyTable, accentBaseKey); + default: + return Qt::Key_unknown; + }; +} + +template<class T> +std::optional<QString> findKeyTextByKeyId(const T &mappingArray, Qt::Key qtKey) +{ + const auto it = std::find_if(mappingArray.cbegin(), mappingArray.cend(), + [qtKey](const WebKb2QtData &data) { return data.qt == qtKey; }); + return it != mappingArray.cend() ? it->web : std::optional<QString>(); +} +} // namespace + +std::optional<Qt::Key> QWasmKeyTranslator::mapWebKeyTextToQtKey(const char *toFind) +{ + const WebKb2QtData searchKey{ toFind, 0 }; + const auto it = std::lower_bound(WebToQtKeyCodeMappings.cbegin(), WebToQtKeyCodeMappings.cend(), + searchKey); + return it != WebToQtKeyCodeMappings.cend() && searchKey == *it ? static_cast<Qt::Key>(it->qt) + : std::optional<Qt::Key>(); +} + +QWasmDeadKeySupport::QWasmDeadKeySupport() = default; + +QWasmDeadKeySupport::~QWasmDeadKeySupport() = default; + +void QWasmDeadKeySupport::applyDeadKeyTranslations(KeyEvent *event) +{ + if (event->deadKey || event->key == Qt::Key_AltGr) { + if (event->modifiers.testFlag(Qt::ShiftModifier) && event->key == Qt::Key_QuoteLeft) + event->key = Qt::Key_AsciiTilde; + m_activeDeadKey = event->key; + } else if (m_activeDeadKey != Qt::Key_unknown + && (m_keyModifiedByDeadKeyOnPress == Qt::Key_unknown + || m_keyModifiedByDeadKeyOnPress == event->key)) { + const Qt::Key baseKey = event->key; + const Qt::Key translatedKey = translateBaseKeyUsingDeadKey(baseKey, m_activeDeadKey); + if (translatedKey != Qt::Key_unknown) + event->key = translatedKey; + + if (auto foundText = event->modifiers.testFlag(Qt::ShiftModifier) + ? findKeyTextByKeyId(WebToQtKeyCodeMappingsWithShift, event->key) + : findKeyTextByKeyId(WebToQtKeyCodeMappings, event->key)) { + if (event->type == EventType::KeyDown) { + Q_ASSERT(m_keyModifiedByDeadKeyOnPress == Qt::Key_unknown); + m_activeDeadKey = Qt::Key_unknown; + } else { + Q_ASSERT(event->type == EventType::KeyUp); + Q_ASSERT(m_keyModifiedByDeadKeyOnPress == baseKey); + m_activeDeadKey = Qt::Key_unknown; + m_keyModifiedByDeadKeyOnPress = Qt::Key_unknown; + } + event->text = foundText->size() == 1 ? *foundText : QString(); + return; + } + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmkeytranslator.h b/src/plugins/platforms/wasm/qwasmkeytranslator.h new file mode 100644 index 0000000000..11a89e6193 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmkeytranslator.h @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef QWASMKEYTRANSLATOR_H +#define QWASMKEYTRANSLATOR_H + +#include <QtCore/qnamespace.h> +#include <QtCore/qtypes.h> + +#include <optional> + +QT_BEGIN_NAMESPACE + +struct KeyEvent; + +namespace QWasmKeyTranslator { +std::optional<Qt::Key> mapWebKeyTextToQtKey(const char *toFind); +} + +class QWasmDeadKeySupport +{ +public: + explicit QWasmDeadKeySupport(); + ~QWasmDeadKeySupport(); + + void applyDeadKeyTranslations(KeyEvent *event); + +private: + Qt::Key m_activeDeadKey = Qt::Key_unknown; + Qt::Key m_keyModifiedByDeadKeyOnPress = Qt::Key_unknown; +}; + +QT_END_NAMESPACE +#endif // QWASMKEYTRANSLATOR_H diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp index add4806e44..cc7c0c43f0 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.cpp +++ b/src/plugins/platforms/wasm/qwasmscreen.cpp @@ -2,11 +2,12 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qwasmscreen.h" -#include "qwasmwindow.h" -#include "qwasmeventtranslator.h" + #include "qwasmcompositor.h" -#include "qwasmintegration.h" #include "qwasmcssstyle.h" +#include "qwasmintegration.h" +#include "qwasmkeytranslator.h" +#include "qwasmwindow.h" #include <emscripten/bind.h> #include <emscripten/val.h> @@ -29,7 +30,7 @@ QWasmScreen::QWasmScreen(const emscripten::val &containerOrCanvas) : m_container(containerOrCanvas), m_shadowContainer(emscripten::val::undefined()), m_compositor(new QWasmCompositor(this)), - m_eventTranslator(new QWasmEventTranslator()) + m_deadKeySupport(std::make_unique<QWasmDeadKeySupport>()) { auto document = m_container["ownerDocument"]; // Each screen is represented by a div container. All of the windows exist therein as @@ -111,11 +112,6 @@ QWasmCompositor *QWasmScreen::compositor() return m_compositor.get(); } -QWasmEventTranslator *QWasmScreen::eventTranslator() -{ - return m_eventTranslator.get(); -} - emscripten::val QWasmScreen::element() const { return m_shadowContainer; diff --git a/src/plugins/platforms/wasm/qwasmscreen.h b/src/plugins/platforms/wasm/qwasmscreen.h index a87fa9156d..98be384522 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.h +++ b/src/plugins/platforms/wasm/qwasmscreen.h @@ -20,7 +20,7 @@ class QPlatformOpenGLContext; class QWasmWindow; class QWasmBackingStore; class QWasmCompositor; -class QWasmEventTranslator; +class QWasmDeadKeySupport; class QOpenGLContext; class QWasmScreen : public QObject, public QPlatformScreen @@ -38,7 +38,7 @@ public: QString outerScreenId() const; QWasmCompositor *compositor(); - QWasmEventTranslator *eventTranslator(); + QWasmDeadKeySupport *deadKeySupport() { return m_deadKeySupport.get(); } QRect geometry() const override; int depth() const override; @@ -67,7 +67,7 @@ private: emscripten::val m_container; emscripten::val m_shadowContainer; std::unique_ptr<QWasmCompositor> m_compositor; - std::unique_ptr<QWasmEventTranslator> m_eventTranslator; + std::unique_ptr<QWasmDeadKeySupport> m_deadKeySupport; QRect m_geometry = QRect(0, 0, 100, 100); int m_depth = 32; QImage::Format m_format = QImage::Format_RGB32; diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp index e0156584e5..2e75c39cee 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.cpp +++ b/src/plugins/platforms/wasm/qwasmwindow.cpp @@ -11,6 +11,9 @@ #include "qwasmbase64iconstore.h" #include "qwasmdom.h" +#include "qwasmclipboard.h" +#include "qwasmintegration.h" +#include "qwasmkeytranslator.h" #include "qwasmwindow.h" #include "qwasmwindowclientarea.h" #include "qwasmscreen.h" @@ -31,11 +34,13 @@ QT_BEGIN_NAMESPACE Q_GUI_EXPORT int qt_defaultDpiX(); -QWasmWindow::QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingStore *backingStore) +QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport, + QWasmCompositor *compositor, QWasmBackingStore *backingStore) : QPlatformWindow(w), m_window(w), m_compositor(compositor), m_backingStore(backingStore), + m_deadKeySupport(deadKeySupport), m_document(dom::document()), m_qtWindow(m_document.call<emscripten::val>("createElement", emscripten::val("div"))), m_windowContents(m_document.call<emscripten::val>("createElement", emscripten::val("div"))), @@ -89,15 +94,15 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingSt m_compositor->addWindow(this); - const auto callback = std::function([this](emscripten::val event) { + const auto pointerCallback = std::function([this](emscripten::val event) { if (processPointer(*PointerEvent::fromWeb(event))) event.call<void>("preventDefault"); }); m_pointerEnterCallback = - std::make_unique<qstdweb::EventCallback>(m_qtWindow, "pointerenter", callback); + std::make_unique<qstdweb::EventCallback>(m_qtWindow, "pointerenter", pointerCallback); m_pointerLeaveCallback = - std::make_unique<qstdweb::EventCallback>(m_qtWindow, "pointerleave", callback); + std::make_unique<qstdweb::EventCallback>(m_qtWindow, "pointerleave", pointerCallback); m_dropCallback = std::make_unique<qstdweb::EventCallback>( m_qtWindow, "drop", [this](emscripten::val event) { @@ -110,6 +115,15 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingSt if (processWheel(*WheelEvent::fromWeb(event))) event.call<void>("preventDefault"); }); + + const auto keyCallback = std::function([this](emscripten::val event) { + if (processKey(*KeyEvent::fromWebWithDeadKeyTranslation(event, m_deadKeySupport))) + event.call<void>("preventDefault"); + }); + + m_keyDownCallback = + std::make_unique<qstdweb::EventCallback>(m_qtWindow, "keydown", keyCallback); + m_keyUpCallback = std::make_unique<qstdweb::EventCallback>(m_qtWindow, "keyup", keyCallback); } QWasmWindow::~QWasmWindow() @@ -433,6 +447,26 @@ void QWasmWindow::applyWindowState() setGeometry(newGeom); } +bool QWasmWindow::processKey(const KeyEvent &event) +{ + constexpr bool ProceedToNativeEvent = false; + Q_ASSERT(event.type == EventType::KeyDown || event.type == EventType::KeyUp); + + const auto clipboardResult = + QWasmIntegration::get()->getWasmClipboard()->processKeyboard(event); + + using ProcessKeyboardResult = QWasmClipboard::ProcessKeyboardResult; + if (clipboardResult == ProcessKeyboardResult::NativeClipboardEventNeeded) + return ProceedToNativeEvent; + + const auto result = QWindowSystemInterface::handleKeyEvent( + 0, event.type == EventType::KeyDown ? QEvent::KeyPress : QEvent::KeyRelease, event.key, + event.modifiers, event.text); + return clipboardResult == ProcessKeyboardResult::NativeClipboardEventAndCopiedDataNeeded + ? ProceedToNativeEvent + : result; +} + bool QWasmWindow::processPointer(const PointerEvent &event) { if (event.pointerType != PointerType::Mouse) @@ -548,6 +582,9 @@ void QWasmWindow::requestActivateWindow() if (window()->isTopLevel()) raise(); + + m_canvas.call<void>("focus"); + QPlatformWindow::requestActivateWindow(); } diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h index 7daa74184f..d490c69b97 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.h +++ b/src/plugins/platforms/wasm/qwasmwindow.h @@ -32,13 +32,16 @@ class EventCallback; class ClientArea; struct DragEvent; +struct KeyEvent; struct PointerEvent; +class QWasmDeadKeySupport; struct WheelEvent; class QWasmWindow final : public QPlatformWindow { public: - QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingStore *backingStore); + QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport, QWasmCompositor *compositor, + QWasmBackingStore *backingStore); ~QWasmWindow() final; void destroy(); @@ -95,6 +98,7 @@ private: bool hasMaximizeButton() const; void applyWindowState(); + bool processKey(const KeyEvent &event); bool processPointer(const PointerEvent &event); bool processDrop(const DragEvent &event); bool processWheel(const WheelEvent &event); @@ -102,6 +106,7 @@ private: QWindow *m_window = nullptr; QWasmCompositor *m_compositor = nullptr; QWasmBackingStore *m_backingStore = nullptr; + QWasmDeadKeySupport *m_deadKeySupport; QRect m_normalGeometry {0, 0, 0 ,0}; emscripten::val m_document; @@ -115,6 +120,9 @@ private: std::unique_ptr<NonClientArea> m_nonClientArea; std::unique_ptr<ClientArea> m_clientArea; + std::unique_ptr<qstdweb::EventCallback> m_keyDownCallback; + std::unique_ptr<qstdweb::EventCallback> m_keyUpCallback; + std::unique_ptr<qstdweb::EventCallback> m_pointerLeaveCallback; std::unique_ptr<qstdweb::EventCallback> m_pointerEnterCallback; |