Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwaylandinputmethodcontext.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 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// Qt-Security score:significant reason:default
4
9
10#include <QtGui/qguiapplication.h>
11#include <QtGui/qtextformat.h>
12#include <QtGui/private/qguiapplication_p.h>
13
15
16namespace QtWaylandClient {
17
18static constexpr int maxStringSize = 1000; // actual max is 4096/3
19
20QWaylandTextInputMethod::QWaylandTextInputMethod(QWaylandDisplay *display, struct ::qt_text_input_method_v1 *textInputMethod)
21 : QtWayland::qt_text_input_method_v1(textInputMethod)
22{
23 Q_UNUSED(display);
24}
25
27{
28 qt_text_input_method_v1_destroy(object());
29}
30
32{
33 if (m_isVisible != visible) {
34 m_isVisible = visible;
35 QGuiApplicationPrivate::platformIntegration()->inputContext()->emitInputPanelVisibleChanged();
36 }
37}
38
40{
41 m_locale = QLocale(localeName);
42}
43
45{
46 m_layoutDirection = Qt::LayoutDirection(inputDirection);
47}
48
49void QWaylandTextInputMethod::text_input_method_v1_keyboard_rectangle_changed(wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height)
50{
51 const QRectF keyboardRectangle(wl_fixed_to_double(x),
52 wl_fixed_to_double(y),
53 wl_fixed_to_double(width),
54 wl_fixed_to_double(height));
55 if (m_keyboardRect != keyboardRectangle) {
56 m_keyboardRect = keyboardRectangle;
57 QGuiApplicationPrivate::platformIntegration()->inputContext()->emitKeyboardRectChanged();
58 }
59}
60
61void QWaylandTextInputMethod::text_input_method_v1_start_input_method_event(uint32_t serial, int32_t surrounding_text_offset)
62{
63 if (m_pendingInputMethodEvents.contains(serial)) {
64 qCWarning(qLcQpaInputMethods) << "Input method event with serial" << serial << "already started";
65 return;
66 }
67
68 m_pendingInputMethodEvents[serial] = QList<QInputMethodEvent::Attribute>{};
69 m_offsetFromCompositor[serial] = surrounding_text_offset;
70}
71
72// We need to keep surrounding text below maxStringSize characters, with cursorPos centered in that substring
73
74static int calculateOffset(const QString &text, int cursorPos)
75{
76 int size = text.size();
77 int halfSize = maxStringSize/2;
78 if (size <= maxStringSize || cursorPos < halfSize)
79 return 0;
80 if (cursorPos > size - halfSize)
81 return size - maxStringSize;
82 return cursorPos - halfSize;
83}
84
85static QString mapSurroundingTextToCompositor(const QString &s, int offset)
86{
87 return s.mid(offset, maxStringSize);
88}
89
90static int mapPositionToCompositor(int pos, int offset)
91{
92 return pos - offset;
93}
94
95static int mapPositionFromCompositor(int pos, int offset)
96{
97 return pos + offset;
98}
99
100void QWaylandTextInputMethod::text_input_method_v1_input_method_event_attribute(uint32_t serial, int32_t type, int32_t start, int32_t length, const QString &value)
101{
102 if (!m_pendingInputMethodEvents.contains(serial)) {
103 qCWarning(qLcQpaInputMethods) << "Input method event with serial" << serial << "does not exist";
104 return;
105 }
106
107 int startMapped = mapPositionFromCompositor(start, m_offsetFromCompositor[serial]);
108 QList<QInputMethodEvent::Attribute> &attributes = m_pendingInputMethodEvents[serial];
109 switch (type) {
110 case QInputMethodEvent::Selection:
111 attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::AttributeType(type), startMapped, length));
112 break;
113 case QInputMethodEvent::Cursor:
114 attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::AttributeType(type), start, length, QColor::fromString(value)));
115 break;
116 case QInputMethodEvent::TextFormat:
117 {
118 QTextCharFormat textFormat;
119 textFormat.setProperty(QTextFormat::FontUnderline, true);
120 textFormat.setProperty(QTextFormat::TextUnderlineStyle, QTextCharFormat::SingleUnderline);
121 attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::AttributeType(type), start, length, textFormat));
122 break;
123 }
124 case QInputMethodEvent::Language:
125 case QInputMethodEvent::Ruby:
126 attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::AttributeType(type), start, length, value));
127 break;
128 };
129}
130
131void QWaylandTextInputMethod::sendInputState(QInputMethodQueryEvent *event, Qt::InputMethodQueries queries)
132{
133 int cursorPosition = event->value(Qt::ImCursorPosition).toInt();
134 int anchorPosition = event->value(Qt::ImAnchorPosition).toInt();
135 QString surroundingText = event->value(Qt::ImSurroundingText).toString();
136 int offset = calculateOffset(surroundingText, cursorPosition);
137
138 if (queries & Qt::ImCursorPosition)
139 update_cursor_position(mapPositionToCompositor(cursorPosition, offset));
140 if (queries & Qt::ImSurroundingText)
141 update_surrounding_text(mapSurroundingTextToCompositor(surroundingText, offset), offset);
142 if (queries & Qt::ImAnchorPosition)
143 update_anchor_position(mapPositionToCompositor(anchorPosition, offset));
144 if (queries & Qt::ImAbsolutePosition)
145 update_absolute_position(event->value(Qt::ImAbsolutePosition).toInt()); // do not map: this is the position in the whole document
146}
147
148
149void QWaylandTextInputMethod::text_input_method_v1_end_input_method_event(uint32_t serial, const QString &commitString, const QString &preeditString, int32_t replacementStart, int32_t replacementLength)
150{
151 if (!m_pendingInputMethodEvents.contains(serial)) {
152 qCWarning(qLcQpaInputMethods) << "Input method event with serial" << serial << "does not exist";
153 return;
154 }
155
156 QList<QInputMethodEvent::Attribute> attributes = m_pendingInputMethodEvents.take(serial);
157 m_offsetFromCompositor.remove(serial);
158 if (QGuiApplication::focusObject() != nullptr) {
159 QInputMethodEvent event(preeditString, attributes);
160 event.setCommitString(commitString, replacementStart, replacementLength);
161 QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event);
162 }
163
164 // Send current state to make sure it matches
165 if (QGuiApplication::focusObject() != nullptr) {
166 QInputMethodQueryEvent event(Qt::ImCursorPosition | Qt::ImSurroundingText | Qt::ImAnchorPosition | Qt::ImAbsolutePosition);
167 QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event);
168 sendInputState(&event);
169 }
170
171 acknowledge_input_method();
172}
173
175 int32_t key,
176 int32_t modifiers,
177 int32_t autoRepeat,
178 int32_t count,
179 int32_t nativeScanCode,
180 int32_t nativeVirtualKey,
181 int32_t nativeModifiers,
182 const QString &text)
183{
184 if (QGuiApplication::focusObject() != nullptr) {
185 QKeyEvent event(QKeyEvent::Type(type),
186 key,
187 Qt::KeyboardModifiers(modifiers),
188 nativeScanCode,
189 nativeVirtualKey,
190 nativeModifiers,
191 text,
192 autoRepeat,
193 count);
194 QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event);
195 }
196}
197
198void QWaylandTextInputMethod::text_input_method_v1_enter(struct ::wl_surface *surface)
199{
200 m_surface = surface;
201}
202
203void QWaylandTextInputMethod::text_input_method_v1_leave(struct ::wl_surface *surface)
204{
205 if (surface != m_surface) {
206 qCWarning(qLcQpaInputMethods) << "Got leave event for surface without corresponding enter";
207 } else {
208 m_surface = nullptr;
209 }
210}
211
212QWaylandInputMethodContext::QWaylandInputMethodContext(QWaylandDisplay *display)
213 : m_display(display)
214{
215}
216
220
222{
223 return m_display->textInputMethodManager() != nullptr;
224}
225
227{
228 QWaylandTextInputMethod *inputMethod = textInputMethod();
229 if (inputMethod != nullptr)
230 inputMethod->reset();
231}
232
234{
235 QWaylandTextInputMethod *inputMethod = textInputMethod();
236 if (inputMethod != nullptr)
237 inputMethod->commit();
238
239 m_display->forceRoundTrip();
240}
241
242void QWaylandInputMethodContext::update(Qt::InputMethodQueries queries)
243{
244 wl_surface *currentSurface = m_currentWindow != nullptr && m_currentWindow->handle() != nullptr
245 ? static_cast<QWaylandWindow *>(m_currentWindow->handle())->wlSurface()
246 : nullptr;
247 if (currentSurface != nullptr && !inputMethodAccepted()) {
248 textInputMethod()->disable(currentSurface);
249 m_currentWindow.clear();
250 } else if (currentSurface == nullptr && inputMethodAccepted()) {
251 QWindow *window = QGuiApplication::focusWindow();
252 currentSurface = window != nullptr && window->handle() != nullptr
253 ? static_cast<QWaylandWindow *>(window->handle())->wlSurface()
254 : nullptr;
255 if (currentSurface != nullptr) {
256 textInputMethod()->disable(currentSurface);
257 m_currentWindow = window;
258 }
259 }
260
261 queries &= (Qt::ImEnabled
262 | Qt::ImHints
263 | Qt::ImCursorRectangle
264 | Qt::ImCursorPosition
265 | Qt::ImSurroundingText
266 | Qt::ImCurrentSelection
267 | Qt::ImAnchorPosition
268 | Qt::ImTextAfterCursor
269 | Qt::ImTextBeforeCursor
270 | Qt::ImPreferredLanguage);
271
272 const Qt::InputMethodQueries queriesNeedingOffset = Qt::ImCursorPosition | Qt::ImSurroundingText | Qt::ImAnchorPosition;
273 if (queries & queriesNeedingOffset)
274 queries |= queriesNeedingOffset;
275
276 QWaylandTextInputMethod *inputMethod = textInputMethod();
277 if (inputMethod != nullptr && QGuiApplication::focusObject() != nullptr) {
278 QInputMethodQueryEvent event(queries);
279 QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event);
280
281 inputMethod->start_update(int(queries));
282
283 if (queries & Qt::ImHints)
284 inputMethod->update_hints(event.value(Qt::ImHints).toInt());
285
286 if (queries & Qt::ImCursorRectangle) {
287 QRect rect = event.value(Qt::ImCursorRectangle).toRect();
288 inputMethod->update_cursor_rectangle(rect.x(), rect.y(), rect.width(), rect.height());
289 }
290
291 inputMethod->sendInputState(&event, queries);
292
293 if (queries & Qt::ImPreferredLanguage)
294 inputMethod->update_preferred_language(event.value(Qt::ImPreferredLanguage).toString());
295
296 inputMethod->end_update();
297
298 // ### Should we do a display sync here and ignore all events until it is received?
299 }
300}
301
302void QWaylandInputMethodContext::invokeAction(QInputMethod::Action action, int cursorPosition)
303{
304 QWaylandTextInputMethod *inputMethod = textInputMethod();
305 if (inputMethod != nullptr)
306 inputMethod->invoke_action(int(action), cursorPosition);
307}
308
310{
311 QWaylandTextInputMethod *inputMethod = textInputMethod();
312 if (inputMethod != nullptr)
313 inputMethod->show_input_panel();
314}
315
317{
318 QWaylandTextInputMethod *inputMethod = textInputMethod();
319 if (inputMethod != nullptr)
320 inputMethod->hide_input_panel();
321}
322
324{
325 QWaylandTextInputMethod *inputMethod = textInputMethod();
326 if (inputMethod != nullptr)
327 return inputMethod->isVisible();
328 else
329 return false;
330}
331
333{
334 QWaylandTextInputMethod *inputMethod = textInputMethod();
335 if (inputMethod != nullptr)
336 return inputMethod->keyboardRect();
337 else
338 return QRectF();
339}
340
342{
343 QWaylandTextInputMethod *inputMethod = textInputMethod();
344 if (inputMethod != nullptr)
345 return inputMethod->locale();
346 else
347 return QLocale();
348}
349
351{
352 QWaylandTextInputMethod *inputMethod = textInputMethod();
353 if (inputMethod != nullptr)
354 return inputMethod->inputDirection();
355 else
356 return Qt::LeftToRight;
357}
358
360{
361 QWaylandTextInputMethod *inputMethod = textInputMethod();
362 if (inputMethod == nullptr)
363 return;
364
365 if (inputMethod->isVisible() && !inputMethodAccepted())
366 inputMethod->hide_input_panel();
367
368 QWindow *window = QGuiApplication::focusWindow();
369
370 if (m_currentWindow != nullptr && m_currentWindow->handle() != nullptr) {
371 if (m_currentWindow.data() != window || !inputMethodAccepted()) {
372 auto *surface = static_cast<QWaylandWindow *>(m_currentWindow->handle())->wlSurface();
373 if (surface)
374 inputMethod->disable(surface);
375 m_currentWindow.clear();
376 }
377 }
378
379 if (window != nullptr && window->handle() != nullptr && inputMethodAccepted()) {
380 if (m_currentWindow.data() != window) {
381 auto *surface = static_cast<QWaylandWindow *>(window->handle())->wlSurface();
382 if (surface != nullptr) {
383 inputMethod->enable(surface);
384 m_currentWindow = window;
385 }
386 }
387
388 update(Qt::ImQueryAll);
389 }
390}
391
393{
394 return m_display->defaultInputDevice() ? m_display->defaultInputDevice()->textInputMethod() : nullptr;
395}
396
397} // QtWaylandClient
398
399QT_END_NAMESPACE
400
401#include "moc_qwaylandinputmethodcontext_p.cpp"
void update(Qt::InputMethodQueries) override
Notification on editor updates.
Qt::LayoutDirection inputDirection() const override
bool isInputPanelVisible() const override
Returns input panel visibility status.
void reset() override
Method to be called when input method needs to be reset.
void setFocusObject(QObject *object) override
This virtual method gets called to notify updated focus to object.
void hideInputPanel() override
Request to hide input panel.
QRectF keyboardRect() const override
This function can be reimplemented to return virtual keyboard rectangle in currently active window co...
void showInputPanel() override
Request to show input panel.
bool isValid() const override
Returns input context validity.
void invokeAction(QInputMethod::Action, int cursorPosition) override
Called when the word currently being composed in the input item is tapped by the user.
void text_input_method_v1_start_input_method_event(uint32_t serial, int32_t surrounding_text_offset) override
void text_input_method_v1_key(int32_t type, int32_t key, int32_t modifiers, int32_t autoRepeat, int32_t count, int32_t nativeScanCode, int32_t nativeVirtualKey, int32_t nativeModifiers, const QString &text) override
void text_input_method_v1_input_method_event_attribute(uint32_t serial, int32_t type, int32_t start, int32_t length, const QString &value) override
void text_input_method_v1_end_input_method_event(uint32_t serial, const QString &commitString, const QString &preeditString, int32_t replacementStart, int32_t replacementLength) override
void sendInputState(QInputMethodQueryEvent *state, Qt::InputMethodQueries queries=Qt::ImQueryInput)
void text_input_method_v1_keyboard_rectangle_changed(wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height) override
void text_input_method_v1_input_direction_changed(int32_t inputDirection) override
void text_input_method_v1_locale_changed(const QString &localeName) override
void text_input_method_v1_visible_changed(int32_t visible) override
Combined button and popup list for selecting options.
static int mapPositionFromCompositor(int pos, int offset)
static int calculateOffset(const QString &text, int cursorPos)
static constexpr int maxStringSize
static QString mapSurroundingTextToCompositor(const QString &s, int offset)
static int mapPositionToCompositor(int pos, int offset)