9#include <QLoggingCategory>
10#include <qguiapplication.h>
12#include <qpa/qplatforminputcontext.h>
13#include <qpa/qwindowsysteminterface.h>
14#if QT_CONFIG(clipboard)
17#include <QtGui/qtextobject.h>
21Q_LOGGING_CATEGORY(qLcQpaWasmInputContext,
"qt.qpa.wasm.inputcontext")
23using namespace qstdweb;
25void QWasmInputContext::inputCallback(
emscripten::val event)
27 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO <<
"isComposing : " << event[
"isComposing"].as<
bool>();
29 emscripten::val inputType = event[
"inputType"];
30 if (inputType.isNull() || inputType.isUndefined())
32 const auto inputTypeString = inputType.as<
std::string>();
35 QString inputStr = (!inputData.isNull() && !inputData.isUndefined())
36 ? QString::fromEcmaString(inputData) : QString();
41 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO <<
"inputType : " << inputTypeString;
42 if (!inputTypeString.compare(
"deleteContentBackward")) {
43 QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier);
44 QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyRelease, Qt::Key_Backspace, Qt::NoModifier);
45 event.call<
void>(
"stopImmediatePropagation");
47 }
else if (!inputTypeString.compare(
"deleteContentForward")) {
48 QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier);
49 QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyRelease, Qt::Key_Delete, Qt::NoModifier);
50 event.call<
void>(
"stopImmediatePropagation");
52 }
else if (!inputTypeString.compare(
"insertCompositionText")) {
53 qCDebug(qLcQpaWasmInputContext) <<
"inputString : " << inputStr;
55 event.call<
void>(
"stopImmediatePropagation");
57 }
else if (!inputTypeString.compare(
"insertReplacementText")) {
58 qCDebug(qLcQpaWasmInputContext) <<
"inputString : " << inputStr;
65 insertText(inputStr,
true);
67 event.call<
void>(
"stopImmediatePropagation");
69 }
else if (!inputTypeString.compare(
"deleteCompositionText")) {
70 setPreeditString(
"", 0);
72 event.call<
void>(
"stopImmediatePropagation");
74 }
else if (!inputTypeString.compare(
"insertFromComposition")) {
75 setPreeditString(inputStr, 0);
77 event.call<
void>(
"stopImmediatePropagation");
79 }
else if (!inputTypeString.compare(
"insertText")) {
81 event.call<
void>(
"stopImmediatePropagation");
82#if QT_CONFIG(clipboard)
83 }
else if (!inputTypeString.compare(
"insertFromPaste")) {
84 insertText(QGuiApplication::clipboard()->text());
85 event.call<
void>(
"stopImmediatePropagation");
92 qCWarning(qLcQpaWasmInputContext) << Q_FUNC_INFO <<
"inputType \"" <<
93 inputType.as<std::string>() <<
"\" is not supported in Qt yet";
97void QWasmInputContext::compositionEndCallback(
emscripten::val event)
99 const auto inputStr = QString::fromEcmaString(event[
"data"]);
100 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << inputStr;
102 if (preeditString().isEmpty())
105 if (inputStr != preeditString()) {
106 qCWarning(qLcQpaWasmInputContext) << Q_FUNC_INFO
107 <<
"Composition string" << inputStr
108 <<
"is differ from" << preeditString();
110 commitPreeditAndClear();
113void QWasmInputContext::compositionStartCallback(
emscripten::val event)
116 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO;
122
123
124
125
126
127
128
129
130
131
132
133
134
136void QWasmInputContext::compositionUpdateCallback(
emscripten::val event)
138 const auto compositionStr = QString::fromEcmaString(event[
"data"]);
139 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << compositionStr;
173 setPreeditString(compositionStr, 0);
176QWasmInputContext::QWasmInputContext()
178 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO;
181QWasmInputContext::~QWasmInputContext()
185void QWasmInputContext::update(Qt::InputMethodQueries queries)
187 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << queries;
189 if ((queries & Qt::ImEnabled) && (inputMethodAccepted() != m_inputMethodAccepted)) {
190 if (m_focusObject && !m_preeditString.isEmpty())
191 commitPreeditAndClear();
192 updateInputElement();
194 QPlatformInputContext::update(queries);
197void QWasmInputContext::showInputPanel()
199 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO;
204 updateInputElement();
207void QWasmInputContext::updateGeometry()
209 if (QWasmAccessibility::isEnabled())
212 if (m_inputElement.isNull())
215 const QWindow *focusWindow = QGuiApplication::focusWindow();
216 if (!m_focusObject || !focusWindow || !m_inputMethodAccepted) {
217 m_inputElement[
"style"].set(
"left",
"0px");
218 m_inputElement[
"style"].set(
"top",
"0px");
220 Q_ASSERT(focusWindow);
221 Q_ASSERT(m_focusObject);
222 Q_ASSERT(m_inputMethodAccepted);
224 const QRect inputItemRectangle = QPlatformInputContext::inputItemRectangle().toRect();
225 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO <<
"propagating inputItemRectangle:" << inputItemRectangle;
226 m_inputElement[
"style"].set(
"left", std::to_string(inputItemRectangle.x()) +
"px");
227 m_inputElement[
"style"].set(
"top", std::to_string(inputItemRectangle.y()) +
"px");
228 m_inputElement[
"style"].set(
"width", std::to_string(inputItemRectangle.width()) +
"px");
229 m_inputElement[
"style"].set(
"height", std::to_string(inputItemRectangle.height()) +
"px");
233void QWasmInputContext::updateInputElement()
235 m_inputMethodAccepted = inputMethodAccepted();
237 if (QWasmAccessibility::isEnabled())
245 QWasmWindow *focusWindow = QWasmWindow::fromWindow(QGuiApplication::focusWindow());
246 if (!m_focusObject || !focusWindow || !m_inputMethodAccepted) {
247 if (!m_inputElement.isNull()) {
248 m_inputElement.set(
"value",
"");
249 m_inputElement.set(
"inputMode", std::string(
"none"));
255 if (!m_inputElement.isNull())
256 m_inputElement.call<
void>(
"blur");
259 m_inputElement = emscripten::val::null();
263 Q_ASSERT(focusWindow);
264 Q_ASSERT(m_focusObject);
265 Q_ASSERT(m_inputMethodAccepted);
267 m_inputElement = focusWindow->inputElement();
269 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << QRectF::fromDOMRect(m_inputElement.call<emscripten::val>(
"getBoundingClientRect"));
272 QInputMethodQueryEvent queryEvent(Qt::ImQueryAll);
273 QCoreApplication::sendEvent(m_focusObject, &queryEvent);
274 qCDebug(qLcQpaWasmInputContext) <<
"Qt surrounding text: " << queryEvent.value(Qt::ImSurroundingText).toString();
275 qCDebug(qLcQpaWasmInputContext) <<
"Qt current selection: " << queryEvent.value(Qt::ImCurrentSelection).toString();
276 qCDebug(qLcQpaWasmInputContext) <<
"Qt text before cursor: " << queryEvent.value(Qt::ImTextBeforeCursor).toString();
277 qCDebug(qLcQpaWasmInputContext) <<
"Qt text after cursor: " << queryEvent.value(Qt::ImTextAfterCursor).toString();
278 qCDebug(qLcQpaWasmInputContext) <<
"Qt cursor position: " << queryEvent.value(Qt::ImCursorPosition).toInt();
279 qCDebug(qLcQpaWasmInputContext) <<
"Qt anchor position: " << queryEvent.value(Qt::ImAnchorPosition).toInt();
281 m_inputElement.set(
"value", queryEvent.value(Qt::ImSurroundingText).toString().toStdString());
283 m_inputElement.set(
"selectionStart", queryEvent.value(Qt::ImAnchorPosition).toUInt());
284 m_inputElement.set(
"selectionEnd", queryEvent.value(Qt::ImCursorPosition).toUInt());
286 QInputMethodQueryEvent query((Qt::InputMethodQueries(Qt::ImHints)));
287 QCoreApplication::sendEvent(m_focusObject, &query);
288 if (Qt::InputMethodHints(query.value(Qt::ImHints).toInt()).testFlag(Qt::ImhHiddenText))
289 m_inputElement.set(
"type",
"password");
291 m_inputElement.set(
"type",
"text");
293 m_inputElement.set(
"inputMode", std::string(
"text"));
295 m_inputElement.call<
void>(
"focus");
298void QWasmInputContext::setFocusObject(QObject *object)
300 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << object << inputMethodAccepted();
303 if (m_focusObject && !m_preeditString.isEmpty())
304 commitPreeditAndClear();
306 m_focusObject = object;
308 updateInputElement();
309 QPlatformInputContext::setFocusObject(object);
312void QWasmInputContext::hideInputPanel()
314 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO;
318 updateInputElement();
321void QWasmInputContext::setPreeditString(QString preeditStr,
int replaceSize)
323 m_preeditString = preeditStr;
324 m_replaceSize = replaceSize;
327void QWasmInputContext::insertPreedit()
329 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << m_preeditString;
331 QList<QInputMethodEvent::Attribute> attributes;
333 QInputMethodEvent::Attribute attr_cursor(QInputMethodEvent::Cursor,
334 m_preeditString.length(),
336 attributes.append(attr_cursor);
338 QTextCharFormat format;
339 format.setFontUnderline(
true);
340 format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
341 QInputMethodEvent::Attribute attr_format(QInputMethodEvent::TextFormat,
343 m_preeditString.length(), format);
344 attributes.append(attr_format);
347 QInputMethodEvent e(m_preeditString, attributes);
348 if (m_replaceSize > 0)
349 e.setCommitString(
"", -m_replaceSize, m_replaceSize);
350 QCoreApplication::sendEvent(m_focusObject, &e);
353void QWasmInputContext::commitPreeditAndClear()
355 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << m_preeditString;
357 if (m_preeditString.isEmpty())
360 e.setCommitString(m_preeditString);
361 m_preeditString.clear();
362 QCoreApplication::sendEvent(m_focusObject, &e);
365void QWasmInputContext::insertText(QString inputStr,
bool replace)
368 if (!inputStr.isEmpty()) {
369 const int replaceLen = 0;
371 e.setCommitString(inputStr, -replaceLen, replaceLen);
372 QCoreApplication::sendEvent(m_focusObject, &e);
friend class QWasmCompositor