147 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO <<
"with serial" << serial << m_currentSerial;
154 if (m_cursorPos != m_anchorPos && (m_pendingDeleteBeforeText != 0 || m_pendingDeleteAfterText != 0)) {
155 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO <<
"Ignore done";
156 m_pendingDeleteBeforeText = 0;
157 m_pendingDeleteAfterText = 0;
158 m_pendingPreeditString.clear();
159 m_pendingCommitString.clear();
163 QObject *focusObject = QGuiApplication::focusObject();
168 qCWarning(qLcQpaWaylandTextInput) << Q_FUNC_INFO << serial <<
"Surface is not enabled yet";
172 if ((m_pendingPreeditString == m_currentPreeditString)
173 && (m_pendingCommitString.isEmpty() && m_pendingDeleteBeforeText == 0
174 && m_pendingDeleteAfterText == 0)) {
176 m_pendingPreeditString.clear();
180 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO <<
"PREEDIT" << m_pendingPreeditString.text << m_pendingPreeditString.cursorBegin;
182 QList<QInputMethodEvent::Attribute> attributes;
184 if (m_pendingPreeditString.cursorBegin != -1 ||
185 m_pendingPreeditString.cursorEnd != -1) {
188 QInputMethodEvent::Attribute attribute1(QInputMethodEvent::Cursor,
189 m_pendingPreeditString.cursorBegin,
191 attributes.append(attribute1);
195 QTextCharFormat format;
196 format.setFontUnderline(
true);
197 format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
198 QInputMethodEvent::Attribute attribute2(QInputMethodEvent::TextFormat,
200 m_pendingPreeditString.text.length(), format);
201 attributes.append(attribute2);
203 QInputMethodEvent event(m_pendingPreeditString.text, attributes);
205 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO <<
"DELETE" << m_pendingDeleteBeforeText << m_pendingDeleteAfterText;
206 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO <<
"COMMIT" << m_pendingCommitString;
209 int replaceLength = 0;
210 if (m_pendingDeleteBeforeText != 0 || m_pendingDeleteAfterText != 0) {
213 m_condReselection =
true;
214 const QByteArray &utf8 = QStringView{m_surroundingText}.toUtf8();
215 if (m_cursorPos <
int(m_pendingDeleteBeforeText)) {
216 replaceFrom = -QString::fromUtf8(QByteArrayView{utf8}.first(m_pendingDeleteBeforeText)).size();
217 replaceLength = QString::fromUtf8(QByteArrayView{utf8}.first(m_pendingDeleteBeforeText + m_pendingDeleteAfterText)).size();
219 replaceFrom = -QString::fromUtf8(QByteArrayView{utf8}.sliced(m_cursorPos - m_pendingDeleteBeforeText, m_pendingDeleteBeforeText)).size();
220 replaceLength = QString::fromUtf8(QByteArrayView{utf8}.sliced(m_cursorPos - m_pendingDeleteBeforeText, m_pendingDeleteBeforeText + m_pendingDeleteAfterText)).size();
224 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO <<
"DELETE from " << replaceFrom <<
" length " << replaceLength;
225 event.setCommitString(m_pendingCommitString,
228 m_currentPreeditString = m_pendingPreeditString;
229 m_pendingPreeditString.clear();
230 m_pendingCommitString.clear();
231 m_pendingDeleteBeforeText = 0;
232 m_pendingDeleteAfterText = 0;
233 QCoreApplication::sendEvent(focusObject, &event);
235 if (serial == m_currentSerial)
236 updateState(supportedQueries3, update_state_full);
256 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << queries << flags;
258 if (!QGuiApplication::focusObject())
261 if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle())
264 auto *window =
static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle());
265 auto *surface = window->wlSurface();
266 if (!surface || (surface != m_surface))
269 queries &= supportedQueries3;
270 bool needsCommit =
false;
272 QInputMethodQueryEvent event(queries);
273 QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event);
276 if (!(queries & Qt::ImSurroundingText) && event.value(Qt::ImSurroundingText).toString().isEmpty()) {
280 if (queries & Qt::ImCursorRectangle) {
281 const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect();
282 const QRect &windowRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect);
283 const QRect &nativeRect = QHighDpi::toNativePixels(windowRect, QGuiApplication::focusWindow());
284 const QMargins margins = window->clientSideMargins();
285 const QRect &surfaceRect = nativeRect.translated(margins.left(), margins.top());
286 if (surfaceRect != m_cursorRect) {
287 set_cursor_rectangle(surfaceRect.x(), surfaceRect.y(), surfaceRect.width(), surfaceRect.height());
288 m_cursorRect = surfaceRect;
293 if ((queries & Qt::ImSurroundingText) || (queries & Qt::ImCursorPosition) || (queries & Qt::ImAnchorPosition)) {
294 QString text = event.value(Qt::ImSurroundingText).toString();
295 int cursor = event.value(Qt::ImCursorPosition).toInt();
296 int anchor = event.value(Qt::ImAnchorPosition).toInt();
298 qCDebug(qLcQpaWaylandTextInput) <<
"Original surrounding_text from InputMethodQuery: " << text << cursor << anchor;
303 const int MAX_MESSAGE_SIZE = 4000;
305 const int textSize = text.toUtf8().size();
306 if (textSize > MAX_MESSAGE_SIZE) {
307 qCDebug(qLcQpaWaylandTextInput) <<
"SurroundText size is over "
309 <<
" byte, some text will be clipped.";
310 const int selectionStart = qMin(cursor, anchor);
311 const int selectionEnd = qMax(cursor, anchor);
312 const int selectionLength = selectionEnd - selectionStart;
313 const int selectionSize = QStringView{text}.sliced(selectionStart, selectionLength).toUtf8().size();
316 if (selectionSize > MAX_MESSAGE_SIZE) {
317 if (anchor > cursor) {
319 anchor = MAX_MESSAGE_SIZE;
320 text = text.sliced(selectionStart, selectionLength);
323 cursor = MAX_MESSAGE_SIZE;
324 text = text.sliced(selectionEnd - selectionLength, selectionLength);
332 int selEndSize = QStringView{text}.first(selectionEnd).toUtf8().size();
333 cursor = QWaylandInputMethodEventBuilder::indexToWayland(text, cursor);
334 anchor = QWaylandInputMethodEventBuilder::indexToWayland(text, anchor);
335 if (selEndSize < MAX_MESSAGE_SIZE) {
336 text = QString::fromUtf8(QByteArrayView{text.toUtf8()}.first(MAX_MESSAGE_SIZE));
338 const int startOffset = selEndSize - MAX_MESSAGE_SIZE;
339 text = QString::fromUtf8(QByteArrayView{text.toUtf8()}.sliced(startOffset, MAX_MESSAGE_SIZE));
340 cursor -= startOffset;
341 anchor -= startOffset;
345 cursor = QWaylandInputMethodEventBuilder::indexToWayland(text, cursor);
346 anchor = QWaylandInputMethodEventBuilder::indexToWayland(text, anchor);
348 qCDebug(qLcQpaWaylandTextInput) <<
"Modified surrounding_text: " << text << cursor << anchor;
350 if (m_surroundingText != text || m_cursorPos != cursor || m_anchorPos != anchor) {
351 qCDebug(qLcQpaWaylandTextInput) <<
"Current surrounding_text: " << m_surroundingText << m_cursorPos << m_anchorPos;
352 qCDebug(qLcQpaWaylandTextInput) <<
"New surrounding_text: " << text << cursor << anchor;
354 set_surrounding_text(text, cursor, anchor);
358 if (m_condReselection) {
359 qCDebug(qLcQpaWaylandTextInput) <<
"\"commit\" is disabled when Reselection by changing focus";
360 m_condReselection =
false;
365 m_surroundingText = text;
366 m_cursorPos = cursor;
367 m_anchorPos = anchor;
372 if (queries & Qt::ImHints) {
373 QWaylandInputMethodContentType contentType = QWaylandInputMethodContentType::convertV3(
static_cast<Qt::InputMethodHints>(event.value(Qt::ImHints).toInt()));
374 qCDebug(qLcQpaWaylandTextInput) << m_contentHint << contentType.hint;
375 qCDebug(qLcQpaWaylandTextInput) << m_contentPurpose << contentType.purpose;
377 if (m_contentHint != contentType.hint || m_contentPurpose != contentType.purpose) {
378 qCDebug(qLcQpaWaylandTextInput) <<
"set_content_type: " << contentType.hint << contentType.purpose;
379 set_content_type(contentType.hint, contentType.purpose);
381 m_contentHint = contentType.hint;
382 m_contentPurpose = contentType.purpose;