diff options
Diffstat (limited to 'src/plugins/platforms/wasm/qwasminputcontext.cpp')
-rw-r--r-- | src/plugins/platforms/wasm/qwasminputcontext.cpp | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/src/plugins/platforms/wasm/qwasminputcontext.cpp b/src/plugins/platforms/wasm/qwasminputcontext.cpp new file mode 100644 index 0000000000..ae72e7b7f9 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasminputcontext.cpp @@ -0,0 +1,161 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <emscripten/bind.h> + +#include "qwasminputcontext.h" +#include "qwasmintegration.h" +#include "qwasmplatform.h" +#include <QRectF> +#include <qpa/qplatforminputcontext.h> +#include "qwasmscreen.h" +#include <qguiapplication.h> +#include <qwindow.h> +#include <QKeySequence> +#include <qpa/qwindowsysteminterface.h> + +QT_BEGIN_NAMESPACE +using namespace qstdweb; + +static void inputCallback(emscripten::val event) +{ + // android sends all the characters typed since the user started typing in this element + int length = event["target"]["value"]["length"].as<int>(); + if (length <= 0) + return; + + emscripten::val _incomingCharVal = event["data"]; + if (_incomingCharVal != emscripten::val::undefined() && _incomingCharVal != emscripten::val::null()) { + + QString str = QString::fromStdString(_incomingCharVal.as<std::string>()); + QWasmInputContext *wasmInput = + reinterpret_cast<QWasmInputContext*>(event["target"]["data-qinputcontext"].as<quintptr>()); + wasmInput->inputStringChanged(str, EMSCRIPTEN_EVENT_KEYDOWN, wasmInput); + } + // this clears the input string, so backspaces do not send a character + // but stops suggestions + event["target"].set("value", ""); +} + +EMSCRIPTEN_BINDINGS(clipboard_module) { + function("qtInputContextCallback", &inputCallback); +} + +QWasmInputContext::QWasmInputContext() +{ + emscripten::val document = emscripten::val::global("document"); + m_inputElement = document.call<emscripten::val>("createElement", std::string("input")); + m_inputElement.set("type", "text"); + m_inputElement.set("style", "position:absolute;left:-1000px;top:-1000px"); // offscreen + m_inputElement.set("contenteditable","true"); + + if (platform() == Platform::MacOS || platform() == Platform::iOS) { + auto callback = [=](emscripten::val) { + m_inputElement["parentElement"].call<void>("removeChild", m_inputElement); + inputPanelIsOpen = false; + }; + m_blurEventHandler.reset(new EventCallback(m_inputElement, "blur", callback)); + + } else { + + const std::string inputType = platform() == Platform::Windows ? "textInput" : "input"; + + document.call<void>("addEventListener", inputType, + emscripten::val::module_property("qtInputContextCallback"), + emscripten::val(false)); + m_inputElement.set("data-qinputcontext", + emscripten::val(quintptr(reinterpret_cast<void *>(this)))); + emscripten::val body = document["body"]; + body.call<void>("appendChild", m_inputElement); + } + + QObject::connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, + &QWasmInputContext::focusWindowChanged); +} + +QWasmInputContext::~QWasmInputContext() +{ + if (platform() == Platform::Android || platform() == Platform::Windows) + emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 0, NULL); +} + +void QWasmInputContext::focusWindowChanged(QWindow *focusWindow) +{ + m_focusWindow = focusWindow; +} + +emscripten::val QWasmInputContext::inputHandlerElementForFocusedWindow() +{ + if (!m_focusWindow) + return emscripten::val::undefined(); + return static_cast<QWasmWindow *>(m_focusWindow->handle())->inputHandlerElement(); +} + +void QWasmInputContext::update(Qt::InputMethodQueries queries) +{ + QPlatformInputContext::update(queries); +} + +void QWasmInputContext::showInputPanel() +{ + if (platform() == Platform::Windows + && !inputPanelIsOpen) { // call this only once for win32 + m_inputElement.call<void>("focus"); + return; + } + // this is called each time the keyboard is touched + + // Add the input element as a child of the screen for the + // currently focused window and give it focus. The browser + // will not display the input element, but mobile browsers + // should display the virtual keyboard. Key events will be + // captured by the keyboard event handler installed on the + // screen element. + + if (platform() == Platform::MacOS // keep for compatibility + || platform() == Platform::iOS + || platform() == Platform::Windows) { + emscripten::val inputWrapper = inputHandlerElementForFocusedWindow(); + if (inputWrapper.isUndefined()) + return; + inputWrapper.call<void>("appendChild", m_inputElement); + } + + m_inputElement.call<void>("focus"); + inputPanelIsOpen = true; +} + +void QWasmInputContext::hideInputPanel() +{ + if (QWasmIntegration::get()->touchPoints < 1) + return; + m_inputElement.call<void>("blur"); + inputPanelIsOpen = false; +} + +void QWasmInputContext::inputStringChanged(QString &inputString, int eventType, QWasmInputContext *context) +{ + Q_UNUSED(context) + QKeySequence keys = QKeySequence::fromString(inputString); + Qt::Key thisKey = keys[0].key(); + + // synthesize this keyevent as android is not normal + if (inputString.size() > 2 && (thisKey < Qt::Key_F35 + || thisKey > Qt::Key_Back)) { + inputString.clear(); + } + if (inputString == QStringLiteral("Escape")) { + thisKey = Qt::Key_Escape; + inputString.clear(); + } else if (thisKey == Qt::Key(0)) { + thisKey = Qt::Key_Return; + } + + QWindowSystemInterface::handleKeyEvent( + 0, eventType == EMSCRIPTEN_EVENT_KEYDOWN ? QEvent::KeyPress : QEvent::KeyRelease, + thisKey, keys[0].keyboardModifiers(), + eventType == EMSCRIPTEN_EVENT_KEYDOWN ? inputString : QStringLiteral("")); +} + + +QT_END_NAMESPACE |