diff options
Diffstat (limited to 'src/plugins/platforms/wasm/qwasminputcontext.cpp')
-rw-r--r-- | src/plugins/platforms/wasm/qwasminputcontext.cpp | 309 |
1 files changed, 104 insertions, 205 deletions
diff --git a/src/plugins/platforms/wasm/qwasminputcontext.cpp b/src/plugins/platforms/wasm/qwasminputcontext.cpp index 2df7066b8e2..191e2947629 100644 --- a/src/plugins/platforms/wasm/qwasminputcontext.cpp +++ b/src/plugins/platforms/wasm/qwasminputcontext.cpp @@ -25,105 +25,88 @@ void QWasmInputContext::inputCallback(emscripten::val event) { qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << "isComposing : " << event["isComposing"].as<bool>(); - QString inputStr = (event["data"] != emscripten::val::null() - && event["data"] != emscripten::val::undefined()) ? - QString::fromStdString(event["data"].as<std::string>()) : QString(); - - QWasmInputContext *wasmInput = - reinterpret_cast<QWasmInputContext *>(event["target"]["data-qinputcontext"].as<quintptr>()); - emscripten::val inputType = event["inputType"]; - if (inputType != emscripten::val::null() - && inputType != emscripten::val::undefined()) { - const auto inputTypeString = inputType.as<std::string>(); - // There are many inputTypes for InputEvent - // https://www.w3.org/TR/input-events-1/ - // Some of them should be implemented here later. - qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << "inputType : " << inputTypeString; - if (!inputTypeString.compare("deleteContentBackward")) { - QWindowSystemInterface::handleKeyEvent(0, - QEvent::KeyPress, - Qt::Key_Backspace, - Qt::NoModifier); - QWindowSystemInterface::handleKeyEvent(0, - QEvent::KeyRelease, - Qt::Key_Backspace, - Qt::NoModifier); - event.call<void>("stopImmediatePropagation"); - return; - } else if (!inputTypeString.compare("deleteContentForward")) { - QWindowSystemInterface::handleKeyEvent(0, - QEvent::KeyPress, - Qt::Key_Delete, - Qt::NoModifier); - QWindowSystemInterface::handleKeyEvent(0, - QEvent::KeyRelease, - Qt::Key_Delete, - Qt::NoModifier); - event.call<void>("stopImmediatePropagation"); - return; - } else if (!inputTypeString.compare("insertCompositionText")) { - qCDebug(qLcQpaWasmInputContext) << "inputString : " << inputStr; - wasmInput->insertPreedit(); - event.call<void>("stopImmediatePropagation"); - return; - } else if (!inputTypeString.compare("insertReplacementText")) { - qCDebug(qLcQpaWasmInputContext) << "inputString : " << inputStr; - //auto ranges = event.call<emscripten::val>("getTargetRanges"); - //qCDebug(qLcQpaWasmInputContext) << ranges["length"].as<int>(); - // WA For Korean IME - // insertReplacementText should have targetRanges but - // Safari cannot have it and just it seems to be supposed - // to replace previous input. - wasmInput->insertText(inputStr, true); - - event.call<void>("stopImmediatePropagation"); - return; - } else if (!inputTypeString.compare("deleteCompositionText")) { - wasmInput->setPreeditString("", 0); - wasmInput->insertPreedit(); - event.call<void>("stopImmediatePropagation"); - return; - } else if (!inputTypeString.compare("insertFromComposition")) { - wasmInput->setPreeditString(inputStr, 0); - wasmInput->insertPreedit(); - event.call<void>("stopImmediatePropagation"); - return; - } else if (!inputTypeString.compare("insertText")) { - wasmInput->insertText(inputStr); - event.call<void>("stopImmediatePropagation"); + if (inputType.isNull() || inputType.isUndefined()) + return; + const auto inputTypeString = inputType.as<std::string>(); + + emscripten::val inputData = event["data"]; + QString inputStr = (!inputData.isNull() && !inputData.isUndefined()) + ? QString::fromEcmaString(inputData) : QString(); + + // There are many inputTypes for InputEvent + // https://www.w3.org/TR/input-events-1/ + // Some of them should be implemented here later. + qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << "inputType : " << inputTypeString; + if (!inputTypeString.compare("deleteContentBackward")) { + QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier); + QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyRelease, Qt::Key_Backspace, Qt::NoModifier); + event.call<void>("stopImmediatePropagation"); + return; + } else if (!inputTypeString.compare("deleteContentForward")) { + QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier); + QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyRelease, Qt::Key_Delete, Qt::NoModifier); + event.call<void>("stopImmediatePropagation"); + return; + } else if (!inputTypeString.compare("insertCompositionText")) { + qCDebug(qLcQpaWasmInputContext) << "inputString : " << inputStr; + insertPreedit(); + event.call<void>("stopImmediatePropagation"); + return; + } else if (!inputTypeString.compare("insertReplacementText")) { + qCDebug(qLcQpaWasmInputContext) << "inputString : " << inputStr; + //auto ranges = event.call<emscripten::val>("getTargetRanges"); + //qCDebug(qLcQpaWasmInputContext) << ranges["length"].as<int>(); + // WA For Korean IME + // insertReplacementText should have targetRanges but + // Safari cannot have it and just it seems to be supposed + // to replace previous input. + insertText(inputStr, true); + + event.call<void>("stopImmediatePropagation"); + return; + } else if (!inputTypeString.compare("deleteCompositionText")) { + setPreeditString("", 0); + insertPreedit(); + event.call<void>("stopImmediatePropagation"); + return; + } else if (!inputTypeString.compare("insertFromComposition")) { + setPreeditString(inputStr, 0); + insertPreedit(); + event.call<void>("stopImmediatePropagation"); + return; + } else if (!inputTypeString.compare("insertText")) { + insertText(inputStr); + event.call<void>("stopImmediatePropagation"); #if QT_CONFIG(clipboard) - } else if (!inputTypeString.compare("insertFromPaste")) { - wasmInput->insertText(QGuiApplication::clipboard()->text()); - event.call<void>("stopImmediatePropagation"); - // These can be supported here, - // But now, keyCallback in QWasmWindow - // will take them as exceptions. - //} else if (!inputTypeString.compare("deleteByCut")) { + } else if (!inputTypeString.compare("insertFromPaste")) { + insertText(QGuiApplication::clipboard()->text()); + event.call<void>("stopImmediatePropagation"); + // These can be supported here, + // But now, keyCallback in QWasmWindow + // will take them as exceptions. + //} else if (!inputTypeString.compare("deleteByCut")) { #endif - } else { - qCWarning(qLcQpaWasmInputContext) << Q_FUNC_INFO << "inputType \"" << inputType.as<std::string>() << "\" is not supported in Qt yet"; - } + } else { + qCWarning(qLcQpaWasmInputContext) << Q_FUNC_INFO << "inputType \"" << + inputType.as<std::string>() << "\" is not supported in Qt yet"; } } void QWasmInputContext::compositionEndCallback(emscripten::val event) { - const auto inputStr = QString::fromStdString(event["data"].as<std::string>()); + const auto inputStr = QString::fromEcmaString(event["data"]); qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << inputStr; - QWasmInputContext *wasmInput = - reinterpret_cast<QWasmInputContext *>(event["target"]["data-qinputcontext"].as<quintptr>()); - - if (wasmInput->preeditString().isEmpty()) + if (preeditString().isEmpty()) return; - if (inputStr != wasmInput->preeditString()) { + if (inputStr != preeditString()) { qCWarning(qLcQpaWasmInputContext) << Q_FUNC_INFO << "Composition string" << inputStr - << "is differ from" << wasmInput->preeditString(); + << "is differ from" << preeditString(); } - wasmInput->commitPreeditAndClear(); + commitPreeditAndClear(); } void QWasmInputContext::compositionStartCallback(emscripten::val event) @@ -151,19 +134,15 @@ static void beforeInputCallback(emscripten::val event) void QWasmInputContext::compositionUpdateCallback(emscripten::val event) { - const auto compositionStr = QString::fromStdString(event["data"].as<std::string>()); + const auto compositionStr = QString::fromEcmaString(event["data"]); qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << compositionStr; - QWasmInputContext *wasmInput = - reinterpret_cast<QWasmInputContext *>(event["target"]["data-qinputcontext"].as<quintptr>()); - // WA for IOS. // Not sure now because I cannot test it anymore. // int replaceSize = 0; // emscripten::val win = emscripten::val::global("window"); // emscripten::val sel = win.call<emscripten::val>("getSelection"); -// if (sel != emscripten::val::null() -// && sel != emscripten::val::undefined() +// if (!sel.isNull() && !sel.isUndefined() // && sel["rangeCount"].as<int>() > 0) { // QInputMethodQueryEvent queryEvent(Qt::ImQueryAll); // QCoreApplication::sendEvent(QGuiApplication::focusObject(), &queryEvent); @@ -172,8 +151,8 @@ void QWasmInputContext::compositionUpdateCallback(emscripten::val event) // qCDebug(qLcQpaWasmInputContext) << "Qt text before cursor: " << queryEvent.value(Qt::ImTextBeforeCursor).toString(); // qCDebug(qLcQpaWasmInputContext) << "Qt text after cursor: " << queryEvent.value(Qt::ImTextAfterCursor).toString(); // -// const QString &selectedStr = QString::fromUtf8(sel.call<emscripten::val>("toString").as<std::string>()); -// const auto &preeditStr = wasmInput->preeditString(); +// const QString &selectedStr = QString::fromEcmaString(sel.call<emscripten::val>("toString")); +// const auto &preeditStr = preeditString(); // qCDebug(qLcQpaWasmInputContext) << "Selection.type : " << sel["type"].as<std::string>(); // qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << "Selected: " << selectedStr; // qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << "PreeditString: " << preeditStr; @@ -189,90 +168,13 @@ void QWasmInputContext::compositionUpdateCallback(emscripten::val event) // qCDebug(qLcQpaWasmInputContext) << "Range.endOffset : " << range["endOffset"].as<int>(); // } // -// wasmInput->setPreeditString(compositionStr, replaceSize); - wasmInput->setPreeditString(compositionStr, 0); -} - -#if QT_CONFIG(clipboard) -static void copyCallback(emscripten::val event) -{ - qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO; - - QClipboard *clipboard = QGuiApplication::clipboard(); - QString inputStr = clipboard->text(); - qCDebug(qLcQpaWasmInputContext) << "QClipboard : " << inputStr; - event["clipboardData"].call<void>("setData", - emscripten::val("text/plain"), - inputStr.toStdString()); - event.call<void>("preventDefault"); - event.call<void>("stopImmediatePropagation"); -} - -static void cutCallback(emscripten::val event) -{ - qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO; - - QClipboard *clipboard = QGuiApplication::clipboard(); - QString inputStr = clipboard->text(); - qCDebug(qLcQpaWasmInputContext) << "QClipboard : " << inputStr; - event["clipboardData"].call<void>("setData", - emscripten::val("text/plain"), - inputStr.toStdString()); - event.call<void>("preventDefault"); - event.call<void>("stopImmediatePropagation"); +// setPreeditString(compositionStr, replaceSize); + setPreeditString(compositionStr, 0); } -static void pasteCallback(emscripten::val event) -{ - qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO; - - emscripten::val clipboardData = event["clipboardData"].call<emscripten::val>("getData", emscripten::val("text/plain")); - QString clipboardStr = QString::fromStdString(clipboardData.as<std::string>()); - qCDebug(qLcQpaWasmInputContext) << "wasm clipboard : " << clipboardStr; - QClipboard *clipboard = QGuiApplication::clipboard(); - if (clipboard->text() != clipboardStr) - clipboard->setText(clipboardStr); - - // propagate to input event (insertFromPaste) -} -#endif // QT_CONFIG(clipboard) - QWasmInputContext::QWasmInputContext() { qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO; - emscripten::val document = emscripten::val::global("document"); - // This 'input' can be an issue to handle multiple lines, - // 'textarea' can be used instead. - m_inputElement = document.call<emscripten::val>("createElement", std::string("input")); - m_inputElement.set("type", "text"); - m_inputElement.set("contenteditable","true"); - m_inputElement.call<void>("setAttribute", std::string("aria-hidden"), std::string("true")); - - m_inputElement["style"].set("position", "absolute"); - m_inputElement["style"].set("left", 0); - m_inputElement["style"].set("top", 0); - m_inputElement["style"].set("opacity", 0); - m_inputElement["style"].set("display", ""); - m_inputElement["style"].set("z-index", -2); - m_inputElement["style"].set("width", "1px"); - m_inputElement["style"].set("height", "1px"); - - m_inputElement.set("data-qinputcontext", - emscripten::val(quintptr(reinterpret_cast<void *>(this)))); - emscripten::val body = document["body"]; - body.call<void>("appendChild", m_inputElement); - - m_inputCallback = QWasmEventHandler(m_inputElement, "input", QWasmInputContext::inputCallback); - m_compositionEndCallback = QWasmEventHandler(m_inputElement, "compositionend", QWasmInputContext::compositionEndCallback); - m_compositionStartCallback = QWasmEventHandler(m_inputElement, "compositionstart", QWasmInputContext::compositionStartCallback); - m_compositionUpdateCallback = QWasmEventHandler(m_inputElement, "compositionupdate", QWasmInputContext::compositionUpdateCallback); - -#if QT_CONFIG(clipboard) - // Clipboard for InputContext - m_clipboardCut = QWasmEventHandler(m_inputElement, "cut", cutCallback); - m_clipboardCopy = QWasmEventHandler(m_inputElement, "copy", copyCallback); - m_clipboardPaste = QWasmEventHandler(m_inputElement, "paste", pasteCallback); -#endif } QWasmInputContext::~QWasmInputContext() @@ -303,6 +205,9 @@ void QWasmInputContext::showInputPanel() void QWasmInputContext::updateGeometry() { + if (m_inputElement.isNull()) + return; + const QWindow *focusWindow = QGuiApplication::focusWindow(); if (!m_focusObject || !focusWindow || !m_inputMethodAccepted) { m_inputElement["style"].set("left", "0px"); @@ -312,23 +217,12 @@ void QWasmInputContext::updateGeometry() Q_ASSERT(m_focusObject); Q_ASSERT(m_inputMethodAccepted); - // Set the geometry - QPoint globalPos; - const QRect cursorRectangle = QPlatformInputContext::cursorRectangle().toRect(); - if (cursorRectangle.isValid()) { - qCDebug(qLcQpaWasmInputContext) - << Q_FUNC_INFO << "cursorRectangle: " << cursorRectangle; - globalPos = focusWindow->mapToGlobal(cursorRectangle.topLeft()); - if (globalPos.x() > 0) - globalPos.setX(globalPos.x() - 1); - if (globalPos.y() > 0) - globalPos.setY(globalPos.y() - 1); - } - - const auto styleLeft = std::to_string(globalPos.x()) + "px"; - const auto styleTop = std::to_string(globalPos.y()) + "px"; - m_inputElement["style"].set("left", styleLeft); - m_inputElement["style"].set("top", styleTop); + const QRect inputItemRectangle = QPlatformInputContext::inputItemRectangle().toRect(); + qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << "propagating inputItemRectangle:" << inputItemRectangle; + m_inputElement["style"].set("left", std::to_string(inputItemRectangle.x()) + "px"); + m_inputElement["style"].set("top", std::to_string(inputItemRectangle.y()) + "px"); + m_inputElement["style"].set("width", std::to_string(inputItemRectangle.width()) + "px"); + m_inputElement["style"].set("height", std::to_string(inputItemRectangle.height()) + "px"); } } @@ -341,16 +235,21 @@ void QWasmInputContext::updateInputElement() updateGeometry(); // If there is no focus object, or no visible input panel, remove focus - const QWindow *focusWindow = QGuiApplication::focusWindow(); + QWasmWindow *focusWindow = QWasmWindow::fromWindow(QGuiApplication::focusWindow()); if (!m_focusObject || !focusWindow || !m_inputMethodAccepted) { - m_inputElement.set("value", ""); + if (!m_inputElement.isNull()) { + m_inputElement.set("value", ""); + m_inputElement.set("inputMode", std::string("none")); + } - if (QWasmWindow *wasmwindow = QWasmWindow::fromWindow(focusWindow)) - wasmwindow->focus(); - else - m_inputElement.call<void>("blur"); + if (focusWindow) { + focusWindow->focus(); + } else { + if (!m_inputElement.isNull()) + m_inputElement.call<void>("blur"); + } - m_inputElement.set("inputMode", std::string("none")); + m_inputElement = emscripten::val::null(); return; } @@ -358,6 +257,8 @@ void QWasmInputContext::updateInputElement() Q_ASSERT(m_focusObject); Q_ASSERT(m_inputMethodAccepted); + m_inputElement = focusWindow->inputElement(); + qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << QRectF::fromDOMRect(m_inputElement.call<emscripten::val>("getBoundingClientRect")); // Set the text input @@ -375,7 +276,15 @@ void QWasmInputContext::updateInputElement() m_inputElement.set("selectionStart", queryEvent.value(Qt::ImAnchorPosition).toUInt()); m_inputElement.set("selectionEnd", queryEvent.value(Qt::ImCursorPosition).toUInt()); + QInputMethodQueryEvent query((Qt::InputMethodQueries(Qt::ImHints))); + QCoreApplication::sendEvent(m_focusObject, &query); + if (Qt::InputMethodHints(query.value(Qt::ImHints).toInt()).testFlag(Qt::ImhHiddenText)) + m_inputElement.set("type", "password"); + else + m_inputElement.set("type", "text"); + m_inputElement.set("inputMode", std::string("text")); + m_inputElement.call<void>("focus"); } @@ -383,16 +292,6 @@ void QWasmInputContext::setFocusObject(QObject *object) { qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << object << inputMethodAccepted(); - QInputMethodQueryEvent query(Qt::InputMethodQueries(Qt::ImEnabled | Qt::ImHints)); - QCoreApplication::sendEvent(object, &query); - if (query.value(Qt::ImEnabled).toBool() - && Qt::InputMethodHints(query.value(Qt::ImHints).toInt()).testFlag(Qt::ImhHiddenText)) { - m_inputElement.set("type", "password"); - } else { - if (m_inputElement["type"].as<std::string>() != std::string("text")) - m_inputElement.set("type", "text"); - } - // Commit the previous composition before change m_focusObject if (m_focusObject && !m_preeditString.isEmpty()) commitPreeditAndClear(); |