From 668c086eff3ad6330ffa0133364d0845efdd5947 Mon Sep 17 00:00:00 2001 From: Inho Lee Date: Mon, 22 Jan 2024 13:28:05 +0100 Subject: [PATCH] TextInputv3: fix indices and positions Indices and positions in wayland are byte size But some when qtwayland uses QString the size conversion was incorrect. Task-number: QTBUG-121446 Pick-to: 6.7 Change-Id: I4e25fb437557a2d6556050599d582ffacabcedbc Reviewed-by: Eskil Abrahamsen Blomfeldt --- .../platforms/wayland/qwaylandtextinputv3.cpp | 97 +++++++++++-------- .../platforms/wayland/qwaylandtextinputv3_p.h | 7 +- .../qwaylandinputmethodeventbuilder.cpp | 4 +- 3 files changed, 63 insertions(+), 45 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylandtextinputv3.cpp b/src/plugins/platforms/wayland/qwaylandtextinputv3.cpp index 017456ac249..bb449c9d60d 100644 --- a/src/plugins/platforms/wayland/qwaylandtextinputv3.cpp +++ b/src/plugins/platforms/wayland/qwaylandtextinputv3.cpp @@ -80,8 +80,8 @@ void QWaylandTextInputv3::zwp_text_input_v3_preedit_string(const QString &text, return; m_pendingPreeditString.text = text; - m_pendingPreeditString.cursorBegin = cursorBegin; - m_pendingPreeditString.cursorEnd = cursorEnd; + m_pendingPreeditString.cursorBegin = QWaylandInputMethodEventBuilder::indexFromWayland(text, cursorBegin); + m_pendingPreeditString.cursorEnd = QWaylandInputMethodEventBuilder::indexFromWayland(text, cursorEnd); } void QWaylandTextInputv3::zwp_text_input_v3_commit_string(const QString &text) @@ -101,8 +101,8 @@ void QWaylandTextInputv3::zwp_text_input_v3_delete_surrounding_text(uint32_t bef if (!QGuiApplication::focusObject()) return; - m_pendingDeleteBeforeText = QWaylandInputMethodEventBuilder::indexFromWayland(m_surroundingText, beforeText); - m_pendingDeleteAfterText = QWaylandInputMethodEventBuilder::indexFromWayland(m_surroundingText, afterText); + m_pendingDeleteBeforeText = beforeText; + m_pendingDeleteAfterText = afterText; } void QWaylandTextInputv3::zwp_text_input_v3_done(uint32_t serial) @@ -157,14 +157,26 @@ void QWaylandTextInputv3::zwp_text_input_v3_done(uint32_t serial) qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "DELETE" << m_pendingDeleteBeforeText << m_pendingDeleteAfterText; qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "COMMIT" << m_pendingCommitString; - // A workaround for reselection - // It will disable redundant commit after reselection - if (m_pendingDeleteBeforeText != 0 || m_pendingDeleteAfterText != 0) + int replaceFrom = 0; + int replaceLength = 0; + if (m_pendingDeleteBeforeText != 0 || m_pendingDeleteAfterText != 0) { + // A workaround for reselection + // It will disable redundant commit after reselection m_condReselection = true; + const QByteArray &utf8 = QStringView{m_surroundingText}.toUtf8(); + if (m_cursorPos < int(m_pendingDeleteBeforeText)) { + replaceFrom = -QString::fromUtf8(QByteArrayView{utf8}.first(m_pendingDeleteBeforeText)).size(); + replaceLength = QString::fromUtf8(QByteArrayView{utf8}.first(m_pendingDeleteBeforeText + m_pendingDeleteAfterText)).size(); + } else { + replaceFrom = -QString::fromUtf8(QByteArrayView{utf8}.sliced(m_cursorPos - m_pendingDeleteBeforeText, m_pendingDeleteBeforeText)).size(); + replaceLength = QString::fromUtf8(QByteArrayView{utf8}.sliced(m_cursorPos - m_pendingDeleteBeforeText, m_pendingDeleteBeforeText + m_pendingDeleteAfterText)).size(); + } + } + qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "DELETE from " << replaceFrom << " length " << replaceLength; event.setCommitString(m_pendingCommitString, - -m_pendingDeleteBeforeText, - m_pendingDeleteBeforeText + m_pendingDeleteAfterText); + replaceFrom, + replaceLength); m_currentPreeditString = m_pendingPreeditString; m_pendingPreeditString.clear(); m_pendingCommitString.clear(); @@ -235,54 +247,63 @@ void QWaylandTextInputv3::updateState(Qt::InputMethodQueries queries, uint32_t f int cursor = event.value(Qt::ImCursorPosition).toInt(); int anchor = event.value(Qt::ImAnchorPosition).toInt(); - qCDebug(qLcQpaWaylandTextInput) << "Orginal surrounding_text from InputMethodQuery: " << text << cursor << anchor; + qCDebug(qLcQpaWaylandTextInput) << "Original surrounding_text from InputMethodQuery: " << text << cursor << anchor; // Make sure text is not too big // surround_text cannot exceed 4000byte in wayland protocol // The worst case will be supposed here. const int MAX_MESSAGE_SIZE = 4000; - if (text.toUtf8().size() > MAX_MESSAGE_SIZE) { - const int selectionStart = QWaylandInputMethodEventBuilder::indexToWayland(text, qMin(cursor, anchor)); - const int selectionEnd = QWaylandInputMethodEventBuilder::indexToWayland(text, qMax(cursor, anchor)); + const int textSize = text.toUtf8().size(); + if (textSize > MAX_MESSAGE_SIZE) { + qCDebug(qLcQpaWaylandTextInput) << "SurroundText size is over " + << MAX_MESSAGE_SIZE + << " byte, some text will be clipped."; + const int selectionStart = qMin(cursor, anchor); + const int selectionEnd = qMax(cursor, anchor); const int selectionLength = selectionEnd - selectionStart; + const int selectionSize = QStringView{text}.sliced(selectionStart, selectionLength).toUtf8().size(); // If selection is bigger than 4000 byte, it is fixed to 4000 byte. // anchor will be moved in the 4000 byte boundary. - if (selectionLength > MAX_MESSAGE_SIZE) { + if (selectionSize > MAX_MESSAGE_SIZE) { if (anchor > cursor) { - const int length = MAX_MESSAGE_SIZE; - anchor = QWaylandInputMethodEventBuilder::trimmedIndexFromWayland(text, length, cursor); - anchor -= cursor; - text = text.mid(cursor, anchor); cursor = 0; + anchor = MAX_MESSAGE_SIZE; + text = text.sliced(selectionStart, selectionLength); } else { - const int length = -MAX_MESSAGE_SIZE; - anchor = QWaylandInputMethodEventBuilder::trimmedIndexFromWayland(text, length, cursor); - cursor -= anchor; - text = text.mid(anchor, cursor); anchor = 0; + cursor = MAX_MESSAGE_SIZE; + text = text.sliced(selectionEnd - selectionLength, selectionLength); } } else { - const int offset = (MAX_MESSAGE_SIZE - selectionLength) / 2; - - int textStart = QWaylandInputMethodEventBuilder::trimmedIndexFromWayland(text, -offset, qMin(cursor, anchor)); - int textEnd = QWaylandInputMethodEventBuilder::trimmedIndexFromWayland(text, MAX_MESSAGE_SIZE, textStart); - - anchor -= textStart; - cursor -= textStart; - text = text.mid(textStart, textEnd - textStart); + // This is not optimal in some cases. + // For examples, if the cursor position and + // the selectionEnd are close to the end of the surround text, + // the tail of the text might always be clipped. + // However all the cases of over 4000 byte are just exceptions. + int selEndSize = QStringView{text}.first(selectionEnd).toUtf8().size(); + cursor = QWaylandInputMethodEventBuilder::indexToWayland(text, cursor); + anchor = QWaylandInputMethodEventBuilder::indexToWayland(text, anchor); + if (selEndSize < MAX_MESSAGE_SIZE) { + text = QString::fromUtf8(QByteArrayView{text.toUtf8()}.first(MAX_MESSAGE_SIZE)); + } else { + const int startOffset = selEndSize - MAX_MESSAGE_SIZE; + text = QString::fromUtf8(QByteArrayView{text.toUtf8()}.sliced(startOffset, MAX_MESSAGE_SIZE)); + cursor -= startOffset; + anchor -= startOffset; + } } + } else { + cursor = QWaylandInputMethodEventBuilder::indexToWayland(text, cursor); + anchor = QWaylandInputMethodEventBuilder::indexToWayland(text, anchor); } qCDebug(qLcQpaWaylandTextInput) << "Modified surrounding_text: " << text << cursor << anchor; - const int cursorPos = QWaylandInputMethodEventBuilder::indexToWayland(text, cursor); - const int anchorPos = QWaylandInputMethodEventBuilder::indexToWayland(text, anchor); - - if (m_surroundingText != text || m_cursorPos != cursorPos || m_anchorPos != anchorPos) { + if (m_surroundingText != text || m_cursorPos != cursor || m_anchorPos != anchor) { qCDebug(qLcQpaWaylandTextInput) << "Current surrounding_text: " << m_surroundingText << m_cursorPos << m_anchorPos; - qCDebug(qLcQpaWaylandTextInput) << "New surrounding_text: " << text << cursorPos << anchorPos; + qCDebug(qLcQpaWaylandTextInput) << "New surrounding_text: " << text << cursor << anchor; - set_surrounding_text(text, cursorPos, anchorPos); + set_surrounding_text(text, cursor, anchor); // A workaround in the case of reselection // It will work when re-clicking a preedit text @@ -294,8 +315,8 @@ void QWaylandTextInputv3::updateState(Qt::InputMethodQueries queries, uint32_t f } m_surroundingText = text; - m_cursorPos = cursorPos; - m_anchorPos = anchorPos; + m_cursorPos = cursor; + m_anchorPos = anchor; m_cursor = cursor; } } diff --git a/src/plugins/platforms/wayland/qwaylandtextinputv3_p.h b/src/plugins/platforms/wayland/qwaylandtextinputv3_p.h index e8b7aa02745..8e32e514dd8 100644 --- a/src/plugins/platforms/wayland/qwaylandtextinputv3_p.h +++ b/src/plugins/platforms/wayland/qwaylandtextinputv3_p.h @@ -17,7 +17,6 @@ #include "qwaylandtextinputinterface_p.h" #include -#include #include struct wl_callback; @@ -63,8 +62,6 @@ protected: void zwp_text_input_v3_done(uint32_t serial) override; private: - QWaylandInputMethodEventBuilder m_builder; - ::wl_surface *m_surface = nullptr; // ### Here for debugging purposes struct PreeditInfo { @@ -82,8 +79,8 @@ private: PreeditInfo m_pendingPreeditString; PreeditInfo m_currentPreeditString; QString m_pendingCommitString; - uint m_pendingDeleteBeforeText = 0; - uint m_pendingDeleteAfterText = 0; + uint m_pendingDeleteBeforeText = 0; // byte length + uint m_pendingDeleteAfterText = 0; // byte length QString m_surroundingText; int m_cursor; // cursor position in QString diff --git a/src/plugins/platforms/wayland/shared/qwaylandinputmethodeventbuilder.cpp b/src/plugins/platforms/wayland/shared/qwaylandinputmethodeventbuilder.cpp index 0f07a69580e..fc422ef04aa 100644 --- a/src/plugins/platforms/wayland/shared/qwaylandinputmethodeventbuilder.cpp +++ b/src/plugins/platforms/wayland/shared/qwaylandinputmethodeventbuilder.cpp @@ -278,10 +278,10 @@ int QWaylandInputMethodEventBuilder::indexFromWayland(const QString &text, int l if (length < 0) { const QByteArray &utf8 = QStringView{text}.left(base).toUtf8(); - return QString::fromUtf8(utf8.left(qMax(utf8.size() + length, 0))).size(); + return QString::fromUtf8(utf8.first(qMax(utf8.size() + length, 0))).size(); } else { const QByteArray &utf8 = QStringView{text}.mid(base).toUtf8(); - return QString::fromUtf8(utf8.left(length)).size() + base; + return QString::fromUtf8(utf8.first(qMin(length, utf8.size()))).size() + base; } }