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