Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwaylandtextinputv3.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include "qwaylandwindow_p.h"
8
9#include <QtCore/qloggingcategory.h>
10#include <QtGui/qguiapplication.h>
11#include <QtGui/private/qhighdpiscaling_p.h>
12#include <QtGui/qevent.h>
13#include <QtGui/qwindow.h>
14#include <QTextCharFormat>
15
17
18Q_LOGGING_CATEGORY(qLcQpaWaylandTextInput, "qt.qpa.wayland.textinput")
19
20namespace QtWaylandClient {
21
22QWaylandTextInputv3::QWaylandTextInputv3(QWaylandDisplay *display,
23 struct ::zwp_text_input_v3 *text_input)
24 : QtWayland::zwp_text_input_v3(text_input)
25{
27}
28
33
34namespace {
35const Qt::InputMethodQueries supportedQueries3 = Qt::ImEnabled |
41}
42
43void QWaylandTextInputv3::zwp_text_input_v3_enter(struct ::wl_surface *surface)
44{
45 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << m_surface << surface;
46
47 m_surface = surface;
48
49 m_pendingPreeditString.clear();
50 m_pendingCommitString.clear();
51 m_pendingDeleteBeforeText = 0;
52 m_pendingDeleteAfterText = 0;
53
54 enable();
55 updateState(supportedQueries3, update_state_enter);
56}
57
58void QWaylandTextInputv3::zwp_text_input_v3_leave(struct ::wl_surface *surface)
59{
60 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO;
61
62 if (m_surface != surface) {
63 qCWarning(qLcQpaWaylandTextInput()) << Q_FUNC_INFO << "Got leave event for surface" << surface << "focused surface" << m_surface;
64 return;
65 }
66
67 m_currentPreeditString.clear();
68
69 m_surface = nullptr;
70
71 disable();
72 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "Done";
73}
74
75void QWaylandTextInputv3::zwp_text_input_v3_preedit_string(const QString &text, int32_t cursorBegin, int32_t cursorEnd)
76{
77 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << text << cursorBegin << cursorEnd;
78
80 return;
81
82 m_pendingPreeditString.text = text;
83 m_pendingPreeditString.cursorBegin = cursorBegin;
84 m_pendingPreeditString.cursorEnd = cursorEnd;
85}
86
88{
89 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << text;
90
92 return;
93
94 m_pendingCommitString = text;
95}
96
97void QWaylandTextInputv3::zwp_text_input_v3_delete_surrounding_text(uint32_t beforeText, uint32_t afterText)
98{
99 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << beforeText << afterText;
100
102 return;
103
104 m_pendingDeleteBeforeText = QWaylandInputMethodEventBuilder::indexFromWayland(m_surroundingText, beforeText);
105 m_pendingDeleteAfterText = QWaylandInputMethodEventBuilder::indexFromWayland(m_surroundingText, afterText);
106}
107
109{
110 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "with serial" << serial << m_currentSerial;
111
112 // This is a case of double click.
113 // text_input_v3 will ignore this done signal and just keep the selection of the clicked word.
114 if (m_cursorPos != m_anchorPos && (m_pendingDeleteBeforeText != 0 || m_pendingDeleteAfterText != 0)) {
115 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "Ignore done";
116 m_pendingDeleteBeforeText = 0;
117 m_pendingDeleteAfterText = 0;
118 m_pendingPreeditString.clear();
119 m_pendingCommitString.clear();
120 return;
121 }
122
123 QObject *focusObject = QGuiApplication::focusObject();
124 if (!focusObject)
125 return;
126
127 if (!m_surface) {
128 qCWarning(qLcQpaWaylandTextInput) << Q_FUNC_INFO << serial << "Surface is not enabled yet";
129 return;
130 }
131
132 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "PREEDIT" << m_pendingPreeditString.text << m_pendingPreeditString.cursorBegin;
133
134 QList<QInputMethodEvent::Attribute> attributes;
135 {
136 if (m_pendingPreeditString.cursorBegin != -1 ||
137 m_pendingPreeditString.cursorEnd != -1) {
138 // Current supported cursor shape is just line.
139 // It means, cursorEnd and cursorBegin are the same.
141 m_pendingPreeditString.text.length(),
142 1);
143 attributes.append(attribute1);
144 }
145
146 // only use single underline style for now
149 format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
151 0,
152 m_pendingPreeditString.text.length(), format);
153 attributes.append(attribute2);
154 }
155 QInputMethodEvent event(m_pendingPreeditString.text, attributes);
156
157 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "DELETE" << m_pendingDeleteBeforeText << m_pendingDeleteAfterText;
158 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "COMMIT" << m_pendingCommitString;
159
160 // A workaround for reselection
161 // It will disable redundant commit after reselection
162 if (m_pendingDeleteBeforeText != 0 || m_pendingDeleteAfterText != 0)
163 m_condReselection = true;
164
165 event.setCommitString(m_pendingCommitString,
166 -m_pendingDeleteBeforeText,
167 m_pendingDeleteBeforeText + m_pendingDeleteAfterText);
168 m_currentPreeditString = m_pendingPreeditString;
169 m_pendingPreeditString.clear();
170 m_pendingCommitString.clear();
171 m_pendingDeleteBeforeText = 0;
172 m_pendingDeleteAfterText = 0;
173 QCoreApplication::sendEvent(focusObject, &event);
174
175 if (serial == m_currentSerial)
176 updateState(supportedQueries3, update_state_full);
177}
178
180{
181 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO;
182
183 m_pendingPreeditString.clear();
184}
185
187{
188 m_currentSerial = (m_currentSerial < UINT_MAX) ? m_currentSerial + 1U: 0U;
189
190 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << "with serial" << m_currentSerial;
191 QtWayland::zwp_text_input_v3::commit();
192}
193
194void QWaylandTextInputv3::updateState(Qt::InputMethodQueries queries, uint32_t flags)
195{
196 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO << queries << flags;
197
199 return;
200
202 return;
203
204 auto *window = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle());
205 auto *surface = window->wlSurface();
206 if (!surface || (surface != m_surface))
207 return;
208
209 queries &= supportedQueries3;
210 bool needsCommit = false;
211
214
215 // For some reason, a query for Qt::ImSurroundingText gives an empty string even though it is not.
216 if (!(queries & Qt::ImSurroundingText) && event.value(Qt::ImSurroundingText).toString().isEmpty()) {
217 return;
218 }
219
220 if (queries & Qt::ImCursorRectangle) {
221 const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect();
222 const QRect &windowRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect);
223 const QRect &nativeRect = QHighDpi::toNativePixels(windowRect, QGuiApplication::focusWindow());
224 const QMargins margins = window->clientSideMargins();
225 const QRect &surfaceRect = nativeRect.translated(margins.left(), margins.top());
226 if (surfaceRect != m_cursorRect) {
227 set_cursor_rectangle(surfaceRect.x(), surfaceRect.y(), surfaceRect.width(), surfaceRect.height());
228 m_cursorRect = surfaceRect;
229 needsCommit = true;
230 }
231 }
232
233 if ((queries & Qt::ImSurroundingText) || (queries & Qt::ImCursorPosition) || (queries & Qt::ImAnchorPosition)) {
234 QString text = event.value(Qt::ImSurroundingText).toString();
235 int cursor = event.value(Qt::ImCursorPosition).toInt();
236 int anchor = event.value(Qt::ImAnchorPosition).toInt();
237
238 qCDebug(qLcQpaWaylandTextInput) << "Orginal surrounding_text from InputMethodQuery: " << text << cursor << anchor;
239
240 // Make sure text is not too big
241 // surround_text cannot exceed 4000byte in wayland protocol
242 // The worst case will be supposed here.
243 const int MAX_MESSAGE_SIZE = 4000;
244
245 if (text.toUtf8().size() > MAX_MESSAGE_SIZE) {
246 const int selectionStart = QWaylandInputMethodEventBuilder::indexToWayland(text, qMin(cursor, anchor));
247 const int selectionEnd = QWaylandInputMethodEventBuilder::indexToWayland(text, qMax(cursor, anchor));
248 const int selectionLength = selectionEnd - selectionStart;
249 // If selection is bigger than 4000 byte, it is fixed to 4000 byte.
250 // anchor will be moved in the 4000 byte boundary.
251 if (selectionLength > MAX_MESSAGE_SIZE) {
252 if (anchor > cursor) {
253 const int length = MAX_MESSAGE_SIZE;
255 anchor -= cursor;
256 text = text.mid(cursor, anchor);
257 cursor = 0;
258 } else {
259 const int length = -MAX_MESSAGE_SIZE;
261 cursor -= anchor;
262 text = text.mid(anchor, cursor);
263 anchor = 0;
264 }
265 } else {
266 const int offset = (MAX_MESSAGE_SIZE - selectionLength) / 2;
267
269 int textEnd = QWaylandInputMethodEventBuilder::trimmedIndexFromWayland(text, MAX_MESSAGE_SIZE, textStart);
270
271 anchor -= textStart;
272 cursor -= textStart;
273 text = text.mid(textStart, textEnd - textStart);
274 }
275 }
276 qCDebug(qLcQpaWaylandTextInput) << "Modified surrounding_text: " << text << cursor << anchor;
277
279 const int anchorPos = QWaylandInputMethodEventBuilder::indexToWayland(text, anchor);
280
281 if (m_surroundingText != text || m_cursorPos != cursorPos || m_anchorPos != anchorPos) {
282 qCDebug(qLcQpaWaylandTextInput) << "Current surrounding_text: " << m_surroundingText << m_cursorPos << m_anchorPos;
283 qCDebug(qLcQpaWaylandTextInput) << "New surrounding_text: " << text << cursorPos << anchorPos;
284
285 set_surrounding_text(text, cursorPos, anchorPos);
286
287 // A workaround in the case of reselection
288 // It will work when re-clicking a preedit text
289 if (m_condReselection) {
290 qCDebug(qLcQpaWaylandTextInput) << "\"commit\" is disabled when Reselection by changing focus";
291 m_condReselection = false;
292 needsCommit = false;
293
294 }
295
296 m_surroundingText = text;
297 m_cursorPos = cursorPos;
298 m_anchorPos = anchorPos;
299 m_cursor = cursor;
300 }
301 }
302
303 if (queries & Qt::ImHints) {
304 QWaylandInputMethodContentType contentType = QWaylandInputMethodContentType::convertV3(static_cast<Qt::InputMethodHints>(event.value(Qt::ImHints).toInt()));
305 qCDebug(qLcQpaWaylandTextInput) << m_contentHint << contentType.hint;
306 qCDebug(qLcQpaWaylandTextInput) << m_contentPurpose << contentType.purpose;
307
308 if (m_contentHint != contentType.hint || m_contentPurpose != contentType.purpose) {
309 qCDebug(qLcQpaWaylandTextInput) << "set_content_type: " << contentType.hint << contentType.purpose;
310 set_content_type(contentType.hint, contentType.purpose);
311
312 m_contentHint = contentType.hint;
313 m_contentPurpose = contentType.purpose;
314 needsCommit = true;
315 }
316 }
317
318 if (needsCommit
320 commit();
321}
322
327
329{
330 return false;
331}
332
334{
335 qCDebug(qLcQpaWaylandTextInput) << Q_FUNC_INFO;
336 return m_cursorRect;
337}
338
340{
341 return QLocale();
342}
343
348
349}
350
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
static bool sendEvent(QObject *receiver, QEvent *event)
Sends event event directly to receiver receiver, using the notify() function.
static QObject * focusObject()
Returns the QObject in currently active window that will be final receiver of events tied to focus,...
static QWindow * focusWindow()
Returns the QWindow that receives events tied to focus, such as key events.
static QInputMethod * inputMethod()
returns the input method.
The QInputMethodEvent class provides parameters for input method events.
Definition qevent.h:625
The QInputMethodQueryEvent class provides an event sent by the input context to input objects.
Definition qevent.h:679
\inmodule QtCore
Definition qmargins.h:24
constexpr int left() const noexcept
Returns the left margin.
Definition qmargins.h:106
constexpr int top() const noexcept
Returns the top margin.
Definition qmargins.h:109
\inmodule QtCore
Definition qobject.h:103
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr QRect translated(int dx, int dy) const noexcept
Returns a copy of the rectangle that is translated dx along the x axis and dy along the y axis,...
Definition qrect.h:261
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
QByteArray toUtf8() const &
Definition qstring.h:634
qsizetype length() const noexcept
Returns the number of characters in this string.
Definition qstring.h:191
void setFontUnderline(bool underline)
If underline is true, sets the text format's font to be underlined; otherwise it is displayed non-und...
static int indexFromWayland(const QString &text, int length, int base=0)
static int trimmedIndexFromWayland(const QString &text, int length, int base=0)
static int indexToWayland(const QString &text, int length, int base=0)
void zwp_text_input_v3_commit_string(const QString &text) override
Qt::LayoutDirection inputDirection() const override
void updateState(Qt::InputMethodQueries queries, uint32_t flags) override
void zwp_text_input_v3_enter(struct ::wl_surface *surface) override
void zwp_text_input_v3_preedit_string(const QString &text, int32_t cursor_begin, int32_t cursor_end) override
void zwp_text_input_v3_delete_surrounding_text(uint32_t before_length, uint32_t after_length) override
void zwp_text_input_v3_leave(struct ::wl_surface *surface) override
void setCursorInsidePreedit(int cursor) override
void zwp_text_input_v3_done(uint32_t serial) override
QString text
QCursor cursor
struct wl_display * display
Definition linuxdmabuf.h:41
T toNativePixels(const T &value, const C *context)
Combined button and popup list for selecting options.
@ ImSurroundingText
@ ImCursorPosition
@ ImAnchorPosition
@ ImCursorRectangle
@ ImHints
@ ImEnabled
LayoutDirection
@ LeftToRight
#define Q_FUNC_INFO
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLuint64 GLenum void * handle
GLenum GLuint GLenum GLsizei length
GLbitfield flags
GLboolean enable
GLenum GLuint GLintptr offset
GLint GLsizei GLsizei GLenum format
struct _cl_event * event
#define Q_UNUSED(x)
aWidget window() -> setWindowTitle("New Window Title")
[2]
static QWaylandInputMethodContentType convertV3(Qt::InputMethodHints hints)