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")) {
44 QInputMethodQueryEvent queryEvent(Qt::ImQueryAll);
45 QCoreApplication::sendEvent(m_focusObject, &queryEvent);
46 int cursorPosition = queryEvent.value(Qt::ImCursorPosition).toInt();
48 int deleteLength = rangesPair.second - rangesPair.first;
50 if (cursorPosition > rangesPair.first) {
51 deleteFrom = -(cursorPosition - rangesPair.first);
54 e.setCommitString(QString(), deleteFrom, deleteLength);
55 QCoreApplication::sendEvent(m_focusObject, &e);
58 rangesPair.second = 0;
60 event.call<
void>(
"stopImmediatePropagation");
62 }
else if (!inputTypeString.compare(
"deleteContentForward")) {
63 QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier);
64 QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyRelease, Qt::Key_Delete, Qt::NoModifier);
65 event.call<
void>(
"stopImmediatePropagation");
67 }
else if (!inputTypeString.compare(
"insertCompositionText")) {
68 qCDebug(qLcQpaWasmInputContext) <<
"inputString : " << inputStr;
70 event.call<
void>(
"stopImmediatePropagation");
72 }
else if (!inputTypeString.compare(
"insertReplacementText")) {
73 qCDebug(qLcQpaWasmInputContext) <<
"inputString : " << inputStr;
80 insertText(inputStr,
true);
82 event.call<
void>(
"stopImmediatePropagation");
84 }
else if (!inputTypeString.compare(
"deleteCompositionText")) {
85 setPreeditString(
"", 0);
87 event.call<
void>(
"stopImmediatePropagation");
89 }
else if (!inputTypeString.compare(
"insertFromComposition")) {
90 setPreeditString(inputStr, 0);
92 event.call<
void>(
"stopImmediatePropagation");
94 }
else if (!inputTypeString.compare(
"insertText")) {
96 event.call<
void>(
"stopImmediatePropagation");
97#if QT_CONFIG(clipboard)
98 }
else if (!inputTypeString.compare(
"insertFromPaste")) {
99 insertText(QGuiApplication::clipboard()->text());
100 event.call<
void>(
"stopImmediatePropagation");
107 qCWarning(qLcQpaWasmInputContext) << Q_FUNC_INFO <<
"inputType \"" <<
108 inputType.as<std::string>() <<
"\" is not supported in Qt yet";
112void QWasmInputContext::compositionEndCallback(
emscripten::val event)
114 const auto inputStr = QString::fromEcmaString(event[
"data"]);
115 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << inputStr;
117 if (preeditString().isEmpty())
120 if (inputStr != preeditString()) {
121 qCWarning(qLcQpaWasmInputContext) << Q_FUNC_INFO
122 <<
"Composition string" << inputStr
123 <<
"is differ from" << preeditString();
125 commitPreeditAndClear();
128void QWasmInputContext::compositionStartCallback(
emscripten::val event)
131 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO;
137
138
139
140
141
142
143
144
145
146
147
148
149
151void QWasmInputContext::compositionUpdateCallback(
emscripten::val event)
153 const auto compositionStr = QString::fromEcmaString(event[
"data"]);
154 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << compositionStr;
156 setPreeditString(compositionStr, 0);
159void QWasmInputContext::beforeInputCallback(
emscripten::val event)
161 emscripten::val ranges = event.call<emscripten::val>(
"getTargetRanges");
163 auto length = ranges[
"length"].as<
int>();
164 for (
auto i = 0; i < length; i++) {
166 qCDebug(qLcQpaWasmInputContext) <<
"startOffset" << range[
"startOffset"].as<
int>();
167 qCDebug(qLcQpaWasmInputContext) <<
"endOffset" << range[
"endOffset"].as<
int>();
168 rangesPair.first = range[
"startOffset"].as<
int>();
169 rangesPair.second = range[
"endOffset"].as<
int>();
173QWasmInputContext::QWasmInputContext()
175 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO;
178QWasmInputContext::~QWasmInputContext()
182void QWasmInputContext::update(Qt::InputMethodQueries queries)
184 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << queries;
186 if ((queries & Qt::ImEnabled) && (inputMethodAccepted() != m_inputMethodAccepted)) {
187 if (m_focusObject && !m_preeditString.isEmpty())
188 commitPreeditAndClear();
189 updateInputElement();
191 QPlatformInputContext::update(queries);
194void QWasmInputContext::showInputPanel()
196 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO;
201 updateInputElement();
204void QWasmInputContext::updateGeometry()
206 if (QWasmAccessibility::isEnabled())
209 if (m_inputElement.isNull())
212 const QWindow *focusWindow = QGuiApplication::focusWindow();
213 if (!m_focusObject || !focusWindow || !m_inputMethodAccepted) {
214 m_inputElement[
"style"].set(
"left",
"0px");
215 m_inputElement[
"style"].set(
"top",
"0px");
217 Q_ASSERT(focusWindow);
218 Q_ASSERT(m_focusObject);
219 Q_ASSERT(m_inputMethodAccepted);
221 const QRect inputItemRectangle = QPlatformInputContext::inputItemRectangle().toRect();
222 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO <<
"propagating inputItemRectangle:" << inputItemRectangle;
223 m_inputElement[
"style"].set(
"left", std::to_string(inputItemRectangle.x()) +
"px");
224 m_inputElement[
"style"].set(
"top", std::to_string(inputItemRectangle.y()) +
"px");
225 m_inputElement[
"style"].set(
"width",
"1px");
226 m_inputElement[
"style"].set(
"height",
"1px");
230void QWasmInputContext::updateInputElement()
232 m_inputMethodAccepted = inputMethodAccepted();
234 if (QWasmAccessibility::isEnabled())
242 QWasmWindow *focusWindow = QWasmWindow::fromWindow(QGuiApplication::focusWindow());
243 if (!m_focusObject || !focusWindow || !m_inputMethodAccepted) {
244 if (!m_inputElement.isNull()) {
245 m_inputElement.set(
"value",
"");
246 m_inputElement.set(
"inputMode", std::string(
"none"));
252 if (!m_inputElement.isNull())
253 m_inputElement.call<
void>(
"blur");
256 m_inputElement = emscripten::val::null();
260 Q_ASSERT(focusWindow);
261 Q_ASSERT(m_focusObject);
262 Q_ASSERT(m_inputMethodAccepted);
264 m_inputElement = focusWindow->inputElement();
266 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << QRectF::fromDOMRect(m_inputElement.call<emscripten::val>(
"getBoundingClientRect"));
269 QInputMethodQueryEvent queryEvent(Qt::ImQueryAll);
270 QCoreApplication::sendEvent(m_focusObject, &queryEvent);
271 qCDebug(qLcQpaWasmInputContext) <<
"Qt surrounding text: " << queryEvent.value(Qt::ImSurroundingText).toString();
272 qCDebug(qLcQpaWasmInputContext) <<
"Qt current selection: " << queryEvent.value(Qt::ImCurrentSelection).toString();
273 qCDebug(qLcQpaWasmInputContext) <<
"Qt text before cursor: " << queryEvent.value(Qt::ImTextBeforeCursor).toString();
274 qCDebug(qLcQpaWasmInputContext) <<
"Qt text after cursor: " << queryEvent.value(Qt::ImTextAfterCursor).toString();
275 qCDebug(qLcQpaWasmInputContext) <<
"Qt cursor position: " << queryEvent.value(Qt::ImCursorPosition).toInt();
276 qCDebug(qLcQpaWasmInputContext) <<
"Qt anchor position: " << queryEvent.value(Qt::ImAnchorPosition).toInt();
278 m_inputElement.set(
"value", queryEvent.value(Qt::ImSurroundingText).toString().toStdString());
280 m_inputElement.set(
"selectionStart", queryEvent.value(Qt::ImAnchorPosition).toUInt());
281 m_inputElement.set(
"selectionEnd", queryEvent.value(Qt::ImCursorPosition).toUInt());
283 QInputMethodQueryEvent query((Qt::InputMethodQueries(Qt::ImHints)));
284 QCoreApplication::sendEvent(m_focusObject, &query);
285 if (Qt::InputMethodHints(query.value(Qt::ImHints).toInt()).testFlag(Qt::ImhHiddenText))
286 m_inputElement.set(
"type",
"password");
288 m_inputElement.set(
"type",
"text");
290 m_inputElement.set(
"inputMode", std::string(
"text"));
292 m_inputElement.call<
void>(
"focus");
295void QWasmInputContext::setFocusObject(QObject *object)
297 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << object << inputMethodAccepted();
300 if (m_focusObject && !m_preeditString.isEmpty())
301 commitPreeditAndClear();
303 m_focusObject = object;
305 updateInputElement();
306 QPlatformInputContext::setFocusObject(object);
309void QWasmInputContext::hideInputPanel()
311 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO;
315 updateInputElement();
318void QWasmInputContext::setPreeditString(QString preeditStr,
int replaceSize)
320 m_preeditString = preeditStr;
321 m_replaceSize = replaceSize;
324void QWasmInputContext::insertPreedit()
326 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << m_preeditString;
328 QList<QInputMethodEvent::Attribute> attributes;
330 QInputMethodEvent::Attribute attr_cursor(QInputMethodEvent::Cursor,
331 m_preeditString.length(),
333 attributes.append(attr_cursor);
335 QTextCharFormat format;
336 format.setFontUnderline(
true);
337 format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
338 QInputMethodEvent::Attribute attr_format(QInputMethodEvent::TextFormat,
340 m_preeditString.length(), format);
341 attributes.append(attr_format);
344 QInputMethodEvent e(m_preeditString, attributes);
345 if (m_replaceSize > 0)
346 e.setCommitString(
"", -m_replaceSize, m_replaceSize);
347 QCoreApplication::sendEvent(m_focusObject, &e);
350void QWasmInputContext::commitPreeditAndClear()
352 qCDebug(qLcQpaWasmInputContext) << Q_FUNC_INFO << m_preeditString;
354 if (m_preeditString.isEmpty())
357 e.setCommitString(m_preeditString);
358 m_preeditString.clear();
359 QCoreApplication::sendEvent(m_focusObject, &e);
362void QWasmInputContext::insertText(QString inputStr,
bool replace)
365 if (!inputStr.isEmpty()) {
366 const int replaceLen = 0;
368 e.setCommitString(inputStr, -replaceLen, replaceLen);
369 QCoreApplication::sendEvent(m_focusObject, &e);
friend class QWasmCompositor
Combined button and popup list for selecting options.