49 Qt::InputMethodQueries queries;
51 if ((other.changedState & Qt::ImHints) && hints != other.hints) {
53 queries |= Qt::ImHints;
56 if ((other.changedState & Qt::ImCursorRectangle) && cursorRectangle != other.cursorRectangle) {
57 cursorRectangle = other.cursorRectangle;
58 queries |= Qt::ImCursorRectangle;
61 if ((other.changedState & Qt::ImSurroundingText) && surroundingText != other.surroundingText) {
62 surroundingText = other.surroundingText;
63 queries |= Qt::ImSurroundingText | Qt::ImCurrentSelection;
66 if ((other.changedState & Qt::ImCursorPosition) && cursorPosition != other.cursorPosition) {
68 queries |= Qt::ImCursorPosition | Qt::ImCurrentSelection;
71 if ((other.changedState & Qt::ImAnchorPosition) && anchorPosition != other.anchorPosition) {
73 queries |= Qt::ImAnchorPosition | Qt::ImCurrentSelection;
76 if ((other.changedState & Qt::ImPreferredLanguage) && preferredLanguage != other.preferredLanguage) {
77 preferredLanguage = other.preferredLanguage;
78 queries |= Qt::ImPreferredLanguage;
91void QWaylandTextInputPrivate::sendInputMethodEvent(QInputMethodEvent *event)
93 Q_Q(QWaylandTextInput);
95 if (!focusResource || !focusResource->handle)
98 QWaylandTextInputClientState afterCommit;
100 afterCommit.surroundingText = currentState->surroundingText;
101 afterCommit.cursorPosition = qMin(currentState->cursorPosition, currentState->anchorPosition);
104 afterCommit.surroundingText.remove(afterCommit.cursorPosition, qAbs(currentState->cursorPosition - currentState->anchorPosition));
106 if (event->replacementLength() > 0 || event->replacementStart() != 0) {
108 afterCommit.cursorPosition = qBound(0, afterCommit.cursorPosition + event->replacementStart(), afterCommit.surroundingText.size());
109 afterCommit.surroundingText.remove(afterCommit.cursorPosition,
110 qMin(event->replacementLength(),
111 afterCommit.surroundingText.size() - afterCommit.cursorPosition));
113 if (event->replacementStart() <= 0 && (event->replacementLength() >= -event->replacementStart())) {
114 const int selectionStart = qMin(currentState->cursorPosition, currentState->anchorPosition);
115 const int selectionEnd = qMax(currentState->cursorPosition, currentState->anchorPosition);
116 const int before = QWaylandInputMethodEventBuilder::indexToWayland(currentState->surroundingText, -event->replacementStart(), selectionStart + event->replacementStart());
117 const int after = QWaylandInputMethodEventBuilder::indexToWayland(currentState->surroundingText, event->replacementLength() + event->replacementStart(), selectionEnd);
118 send_delete_surrounding_text(focusResource->handle, before, after);
121 qWarning() <<
"Not yet supported case of replacement. Start:" << event->replacementStart() <<
"length:" << event->replacementLength();
126 afterCommit.surroundingText.insert(afterCommit.cursorPosition, event->commitString());
127 afterCommit.cursorPosition += event->commitString().size();
128 afterCommit.anchorPosition = afterCommit.cursorPosition;
130 for (
const QInputMethodEvent::Attribute &attribute : event->attributes()) {
131 if (attribute.type == QInputMethodEvent::Selection) {
132 afterCommit.cursorPosition = attribute.start;
133 afterCommit.anchorPosition = attribute.length;
134 int cursor = QWaylandInputMethodEventBuilder::indexToWayland(afterCommit.surroundingText, qAbs(attribute.start - afterCommit.cursorPosition), qMin(attribute.start, afterCommit.cursorPosition));
135 int anchor = QWaylandInputMethodEventBuilder::indexToWayland(afterCommit.surroundingText, qAbs(attribute.length - afterCommit.cursorPosition), qMin(attribute.length, afterCommit.cursorPosition));
136 send_cursor_position(focusResource->handle,
137 attribute.start < afterCommit.cursorPosition ? -cursor : cursor,
138 attribute.length < afterCommit.cursorPosition ? -anchor : anchor);
141 send_commit_string(focusResource->handle, event->commitString());
142 for (
const QInputMethodEvent::Attribute &attribute : event->attributes()) {
143 if (attribute.type == QInputMethodEvent::Cursor) {
144 int index = QWaylandInputMethodEventBuilder::indexToWayland(event->preeditString(), attribute.start);
145 send_preedit_cursor(focusResource->handle, index);
146 }
else if (attribute.type == QInputMethodEvent::TextFormat) {
147 int start = QWaylandInputMethodEventBuilder::indexToWayland(event->preeditString(), attribute.start);
148 int length = QWaylandInputMethodEventBuilder::indexToWayland(event->preeditString(), attribute.length, attribute.start);
150 send_preedit_styling(focusResource->handle, start, length, preedit_style_default);
153 send_preedit_string(focusResource->handle, event->preeditString(), event->preeditString());
155 Qt::InputMethodQueries queries = currentState->updatedQueries(afterCommit);
156 currentState->surroundingText = afterCommit.surroundingText;
157 currentState->cursorPosition = afterCommit.cursorPosition;
158 currentState->anchorPosition = afterCommit.anchorPosition;
161 qCDebug(qLcWaylandCompositorInputMethods) <<
"QInputMethod::update() after QInputMethodEvent" << queries;
163 emit q->updateInputMethod(queries);
167void QWaylandTextInputPrivate::sendKeyEvent(QKeyEvent *event)
169 if (!focusResource || !focusResource->handle)
173 const auto &qtMods = event->modifiers();
174 if (qtMods & Qt::ShiftModifier)
175 mods |= shiftModifierMask;
176 if (qtMods & Qt::ControlModifier)
177 mods |= controlModifierMask;
178 if (qtMods & Qt::AltModifier)
179 mods |= altModifierMask;
180 if (qtMods & Qt::MetaModifier)
181 mods |= metaModifierMask;
183#if QT_CONFIG(xkbcommon)
184 for (xkb_keysym_t keysym : QXkbCommon::toKeysym(event)) {
185 send_keysym(focusResource->handle, event->timestamp(), keysym,
186 event->type() == QEvent::KeyPress ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED,
194void QWaylandTextInputPrivate::sendInputPanelState()
196 if (!focusResource || !focusResource->handle)
199 QInputMethod *inputMethod = qApp->inputMethod();
200 const QRectF& keyboardRect = inputMethod->keyboardRectangle();
201 const QRectF& sceneInputRect = inputMethod->inputItemTransform().mapRect(inputMethod->inputItemRectangle());
202 const QRectF& localRect = sceneInputRect.intersected(keyboardRect).translated(-sceneInputRect.topLeft());
204 send_input_panel_state(focusResource->handle,
205 inputMethod->isVisible() ? input_panel_visibility_visible : input_panel_visibility_hidden,
206 localRect.x(), localRect.y(), localRect.width(), localRect.height());
229QVariant QWaylandTextInputPrivate::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument)
const
233 return QVariant(
static_cast<
int>(currentState->hints));
234 case Qt::ImCursorRectangle:
235 return currentState->cursorRectangle;
239 case Qt::ImCursorPosition:
240 return currentState->cursorPosition;
241 case Qt::ImSurroundingText:
242 return currentState->surroundingText;
243 case Qt::ImCurrentSelection:
244 return currentState->surroundingText.mid(qMin(currentState->cursorPosition, currentState->anchorPosition),
245 qAbs(currentState->anchorPosition - currentState->cursorPosition));
246 case Qt::ImMaximumTextLength:
249 case Qt::ImAnchorPosition:
250 return currentState->anchorPosition;
251 case Qt::ImAbsolutePosition:
253 return currentState->cursorPosition;
254 case Qt::ImTextAfterCursor:
255 if (argument.isValid())
256 return currentState->surroundingText.mid(currentState->cursorPosition, argument.toInt());
257 return currentState->surroundingText.mid(currentState->cursorPosition);
258 case Qt::ImTextBeforeCursor:
259 if (argument.isValid())
260 return currentState->surroundingText.left(currentState->cursorPosition).right(argument.toInt());
261 return currentState->surroundingText.left(currentState->cursorPosition);
262 case Qt::ImPreferredLanguage:
263 return currentState->preferredLanguage;
270void QWaylandTextInputPrivate::setFocus(QWaylandSurface *surface)
272 Q_Q(QWaylandTextInput);
274 if (focusResource && focus != surface) {
275 uint32_t serial = compositor->nextSerial();
276 send_leave(focusResource->handle, serial, focus->resource());
277 focusDestroyListener.reset();
280 Resource *resource = surface ? resourceMap().value(surface->waylandClient()) : 0;
282 if (resource && (focus != surface || focusResource != resource)) {
283 uint32_t serial = compositor->nextSerial();
284 currentState.reset(
new QWaylandTextInputClientState);
285 pendingState.reset(
new QWaylandTextInputClientState);
286 send_enter(resource->handle, serial, surface->resource());
287 focusResource = resource;
288 sendInputPanelState();
291 focusDestroyListener.listenForDestruction(surface->resource());
292 if (inputPanelVisible && q->isSurfaceEnabled(surface))
293 qApp->inputMethod()->show();
296 focusResource = resource;
379void QWaylandTextInputPrivate::zwp_text_input_v2_update_state(Resource *resource, uint32_t serial, uint32_t flags)
381 Q_Q(QWaylandTextInput);
383 qCDebug(qLcWaylandCompositorInputMethods) <<
"update_state" << serial << flags;
385 if (resource != focusResource)
388 if (flags == update_state_reset || flags == update_state_enter) {
389 qCDebug(qLcWaylandCompositorInputMethods) <<
"QInputMethod::reset()";
390 qApp->inputMethod()->reset();
393 this->serial = serial;
395 Qt::InputMethodQueries queries;
396 if (flags == update_state_change) {
397 queries = currentState->mergeChanged(*pendingState);
399 queries = pendingState->updatedQueries(*currentState);
400 currentState.swap(pendingState);
403 pendingState.reset(
new QWaylandTextInputClientState);
406 qCDebug(qLcWaylandCompositorInputMethods) <<
"QInputMethod::update()" << queries;
408 emit q->updateInputMethod(queries);
412void QWaylandTextInputPrivate::zwp_text_input_v2_set_content_type(Resource *resource, uint32_t hint, uint32_t purpose)
414 if (resource != focusResource)
417 pendingState->hints = Qt::ImhNone;
419 if ((hint & content_hint_auto_completion) == 0
420 && (hint & content_hint_auto_correction) == 0)
421 pendingState->hints |= Qt::ImhNoPredictiveText;
422 if ((hint & content_hint_auto_capitalization) == 0)
423 pendingState->hints |= Qt::ImhNoAutoUppercase;
424 if ((hint & content_hint_lowercase) != 0)
425 pendingState->hints |= Qt::ImhPreferLowercase;
426 if ((hint & content_hint_uppercase) != 0)
427 pendingState->hints |= Qt::ImhPreferUppercase;
428 if ((hint & content_hint_hidden_text) != 0)
429 pendingState->hints |= Qt::ImhHiddenText;
430 if ((hint & content_hint_sensitive_data) != 0)
431 pendingState->hints |= Qt::ImhSensitiveData;
432 if ((hint & content_hint_latin) != 0)
433 pendingState->hints |= Qt::ImhLatinOnly;
434 if ((hint & content_hint_multiline) != 0)
435 pendingState->hints |= Qt::ImhMultiLine;
438 case content_purpose_normal:
440 case content_purpose_alpha:
441 pendingState->hints |= Qt::ImhUppercaseOnly | Qt::ImhLowercaseOnly;
443 case content_purpose_digits:
444 pendingState->hints |= Qt::ImhDigitsOnly;
446 case content_purpose_number:
447 pendingState->hints |= Qt::ImhFormattedNumbersOnly;
449 case content_purpose_phone:
450 pendingState->hints |= Qt::ImhDialableCharactersOnly;
452 case content_purpose_url:
453 pendingState->hints |= Qt::ImhUrlCharactersOnly;
455 case content_purpose_email:
456 pendingState->hints |= Qt::ImhEmailCharactersOnly;
458 case content_purpose_name:
459 case content_purpose_password:
461 case content_purpose_date:
462 pendingState->hints |= Qt::ImhDate;
464 case content_purpose_time:
465 pendingState->hints |= Qt::ImhTime;
467 case content_purpose_datetime:
468 pendingState->hints |= Qt::ImhDate | Qt::ImhTime;
470 case content_purpose_terminal:
475 pendingState->changedState |= Qt::ImHints;
501 : QWaylandCompositorExtensionTemplate(container, *
new QWaylandTextInputPrivate(compositor))
503 connect(&d_func()->focusDestroyListener, &QWaylandDestroyListener::fired,
506 connect(qApp->inputMethod(), &QInputMethod::visibleChanged,
508 connect(qApp->inputMethod(), &QInputMethod::keyboardRectangleChanged,
510 connect(qApp->inputMethod(), &QInputMethod::inputDirectionChanged,
512 connect(qApp->inputMethod(), &QInputMethod::localeChanged,
613 Q_D(QWaylandTextInput);
615 const QList<QByteArray> modifiers = modifiersMap.split(
'\0');
617 int numModifiers = modifiers.size();
618 if (modifiers.last().isEmpty())
621 for (
int i = 0; i < numModifiers; ++i) {
622 const auto modString = modifiers.at(i);
623 if (modString == XKB_MOD_NAME_SHIFT)
624 d->shiftModifierMask = 1 << i;
625 else if (modString == XKB_MOD_NAME_CTRL)
626 d->controlModifierMask = 1 << i;
627 else if (modString == XKB_MOD_NAME_ALT)
628 d->altModifierMask = 1 << i;
629 else if (modString == XKB_MOD_NAME_LOGO)
630 d->metaModifierMask = 1 << i;
632 qCDebug(qLcWaylandCompositorInputMethods) <<
"unsupported modifier name " << modString;
634 d->sendModifiersMap(modifiersMap);