138 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO <<
"with serial" << serial << m_currentSerial;
145 if (m_cursorPos != m_anchorPos && (m_pendingDeleteBeforeText != 0 || m_pendingDeleteAfterText != 0)) {
146 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO <<
"Ignore done";
147 m_pendingDeleteBeforeText = 0;
148 m_pendingDeleteAfterText = 0;
149 m_pendingPreeditString.clear();
150 m_pendingCommitString.clear();
154 QObject *focusObject = QGuiApplication::focusObject();
159 qCWarning(qLcQpaWaylandTextInput) << Q_FUNC_INFO << serial <<
"Surface is not enabled yet";
163 if ((m_pendingPreeditString == m_currentPreeditString)
164 && (m_pendingCommitString.isEmpty() && m_pendingDeleteBeforeText == 0
165 && m_pendingDeleteAfterText == 0)) {
167 m_pendingPreeditString.clear();
171 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO <<
"PREEDIT" << m_pendingPreeditString.text << m_pendingPreeditString.cursorBegin;
173 QList<QInputMethodEvent::Attribute> attributes;
175 if (m_pendingPreeditString.cursorBegin != -1 ||
176 m_pendingPreeditString.cursorEnd != -1) {
179 QInputMethodEvent::Attribute attribute1(QInputMethodEvent::Cursor,
180 m_pendingPreeditString.cursorBegin,
182 attributes.append(attribute1);
186 QTextCharFormat format;
187 format.setFontUnderline(
true);
188 format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
189 QInputMethodEvent::Attribute attribute2(QInputMethodEvent::TextFormat,
191 m_pendingPreeditString.text.length(), format);
192 attributes.append(attribute2);
194 QInputMethodEvent event(m_pendingPreeditString.text, attributes);
196 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO <<
"DELETE" << m_pendingDeleteBeforeText << m_pendingDeleteAfterText;
197 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO <<
"COMMIT" << m_pendingCommitString;
200 int replaceLength = 0;
201 if (m_pendingDeleteBeforeText != 0 || m_pendingDeleteAfterText != 0) {
204 m_condReselection =
true;
205 const QByteArray &utf8 = QStringView{m_surroundingText}.toUtf8();
206 if (m_cursorPos <
int(m_pendingDeleteBeforeText)) {
207 replaceFrom = -QString::fromUtf8(QByteArrayView{utf8}.first(m_pendingDeleteBeforeText)).size();
208 replaceLength = QString::fromUtf8(QByteArrayView{utf8}.first(m_pendingDeleteBeforeText + m_pendingDeleteAfterText)).size();
210 replaceFrom = -QString::fromUtf8(QByteArrayView{utf8}.sliced(m_cursorPos - m_pendingDeleteBeforeText, m_pendingDeleteBeforeText)).size();
211 replaceLength = QString::fromUtf8(QByteArrayView{utf8}.sliced(m_cursorPos - m_pendingDeleteBeforeText, m_pendingDeleteBeforeText + m_pendingDeleteAfterText)).size();
215 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO <<
"DELETE from " << replaceFrom <<
" length " << replaceLength;
216 event.setCommitString(m_pendingCommitString,
219 m_currentPreeditString = m_pendingPreeditString;
220 m_pendingPreeditString.clear();
221 m_pendingCommitString.clear();
222 m_pendingDeleteBeforeText = 0;
223 m_pendingDeleteAfterText = 0;
224 QCoreApplication::sendEvent(focusObject, &event);
226 if (serial == m_currentSerial)
227 updateState(supportedQueries3, update_state_full);
247 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << queries << flags;
249 if (!QGuiApplication::focusObject())
252 if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle())
255 auto *window =
static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle());
256 auto *surface = window->wlSurface();
257 if (!surface || (surface != m_surface))
260 queries &= supportedQueries3;
261 bool needsCommit =
false;
263 QInputMethodQueryEvent event(queries);
264 QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event);
267 if (!(queries & Qt::ImSurroundingText) && event.value(Qt::ImSurroundingText).toString().isEmpty()) {
271 if (queries & Qt::ImCursorRectangle) {
272 const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect();
273 const QRect &windowRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect);
274 const QRect &nativeRect = QHighDpi::toNativePixels(windowRect, QGuiApplication::focusWindow());
275 const QMargins margins = window->clientSideMargins();
276 const QRect &surfaceRect = nativeRect.translated(margins.left(), margins.top());
277 if (surfaceRect != m_cursorRect) {
278 set_cursor_rectangle(surfaceRect.x(), surfaceRect.y(), surfaceRect.width(), surfaceRect.height());
279 m_cursorRect = surfaceRect;
284 if ((queries & Qt::ImSurroundingText) || (queries & Qt::ImCursorPosition) || (queries & Qt::ImAnchorPosition)) {
285 QString text = event.value(Qt::ImSurroundingText).toString();
286 int cursor = event.value(Qt::ImCursorPosition).toInt();
287 int anchor = event.value(Qt::ImAnchorPosition).toInt();
289 qCDebug(qLcQpaWaylandTextInput) <<
"Original surrounding_text from InputMethodQuery: " << text << cursor << anchor;
294 const int MAX_MESSAGE_SIZE = 4000;
296 const QByteArray utf8 = text.toUtf8();
297 const int textSize = utf8.size();
298 if (textSize > MAX_MESSAGE_SIZE) {
299 qCDebug(qLcQpaWaylandTextInput) <<
"SurroundText size is over "
301 <<
" byte, some text will be clipped.";
302 const int selectionStart = qMin(cursor, anchor);
303 const int selectionEnd = qMax(cursor, anchor);
304 const int selectionLength = selectionEnd - selectionStart;
305 QByteArray selection = QStringView{text}.sliced(selectionStart, selectionLength).toUtf8();
306 const int selectionSize = selection.size();
309 if (selectionSize > MAX_MESSAGE_SIZE) {
310 if (anchor > cursor) {
312 anchor = MAX_MESSAGE_SIZE;
313 text = QString::fromUtf8(QByteArrayView{selection}.sliced(0, MAX_MESSAGE_SIZE));
316 cursor = MAX_MESSAGE_SIZE;
317 text = QString::fromUtf8(QByteArrayView{selection}.sliced(selectionSize - MAX_MESSAGE_SIZE, MAX_MESSAGE_SIZE));
325 int selEndSize = QStringView{text}.first(selectionEnd).toUtf8().size();
326 cursor = QWaylandInputMethodEventBuilder::indexToWayland(text, cursor);
327 anchor = QWaylandInputMethodEventBuilder::indexToWayland(text, anchor);
328 if (selEndSize < MAX_MESSAGE_SIZE) {
329 text = QString::fromUtf8(QByteArrayView{utf8}.first(MAX_MESSAGE_SIZE));
331 const int startOffset = selEndSize - MAX_MESSAGE_SIZE;
332 text = QString::fromUtf8(QByteArrayView{utf8}.sliced(startOffset, MAX_MESSAGE_SIZE));
333 cursor -= startOffset;
334 anchor -= startOffset;
338 cursor = QWaylandInputMethodEventBuilder::indexToWayland(text, cursor);
339 anchor = QWaylandInputMethodEventBuilder::indexToWayland(text, anchor);
341 qCDebug(qLcQpaWaylandTextInput) <<
"Modified surrounding_text: " << text << cursor << anchor;
343 if (m_surroundingText != text || m_cursorPos != cursor || m_anchorPos != anchor) {
344 qCDebug(qLcQpaWaylandTextInput) <<
"Current surrounding_text: " << m_surroundingText << m_cursorPos << m_anchorPos;
345 qCDebug(qLcQpaWaylandTextInput) <<
"New surrounding_text: " << text << cursor << anchor;
347 set_surrounding_text(text, cursor, anchor);
351 if (m_condReselection) {
352 qCDebug(qLcQpaWaylandTextInput) <<
"\"commit\" is disabled when Reselection by changing focus";
353 m_condReselection =
false;
358 m_surroundingText = text;
359 m_cursorPos = cursor;
360 m_anchorPos = anchor;
365 if (queries & Qt::ImHints) {
366 QWaylandInputMethodContentType contentType = QWaylandInputMethodContentType::convertV3(
static_cast<Qt::InputMethodHints>(event.value(Qt::ImHints).toInt()));
367 qCDebug(qLcQpaWaylandTextInput) << m_contentHint << contentType.hint;
368 qCDebug(qLcQpaWaylandTextInput) << m_contentPurpose << contentType.purpose;
370 if (m_contentHint != contentType.hint || m_contentPurpose != contentType.purpose) {
371 qCDebug(qLcQpaWaylandTextInput) <<
"set_content_type: " << contentType.hint << contentType.purpose;
372 set_content_type(contentType.hint, contentType.purpose);
374 m_contentHint = contentType.hint;
375 m_contentPurpose = contentType.purpose;