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
qwindowsinputcontext.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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 <QtCore/qdebug.h>
11#include <QtCore/qobject.h>
12#include <QtCore/qrect.h>
13#include <QtCore/qtextboundaryfinder.h>
14
15#include <QtGui/qevent.h>
16#include <QtGui/qtextformat.h>
17#include <QtGui/qpalette.h>
18#include <QtGui/qguiapplication.h>
19
20#include <private/qhighdpiscaling_p.h>
21
22#include <algorithm>
23
24QT_BEGIN_NAMESPACE
25
26static inline QByteArray debugComposition(int lParam)
27{
28 QByteArray str;
29 if (lParam & GCS_RESULTSTR)
30 str += "RESULTSTR ";
31 if (lParam & GCS_COMPSTR)
32 str += "COMPSTR ";
33 if (lParam & GCS_COMPATTR)
34 str += "COMPATTR ";
35 if (lParam & GCS_CURSORPOS)
36 str += "CURSORPOS ";
37 if (lParam & GCS_COMPCLAUSE)
38 str += "COMPCLAUSE ";
39 if (lParam & CS_INSERTCHAR)
40 str += "INSERTCHAR ";
41 if (lParam & CS_NOMOVECARET)
42 str += "NOMOVECARET ";
43 return str;
44}
45
46// Cancel current IME composition.
47static inline void imeNotifyCancelComposition(HWND hwnd)
48{
49 if (!hwnd) {
50 qWarning() << __FUNCTION__ << "called with" << hwnd;
51 return;
52 }
53 const HIMC himc = ImmGetContext(hwnd);
54 ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
55 ImmReleaseContext(hwnd, himc);
56}
57
58static inline LCID languageIdFromLocaleId(LCID localeId)
59{
60 return localeId & 0xFFFF;
61}
62
64{
65 return languageIdFromLocaleId(reinterpret_cast<quintptr>(GetKeyboardLayout(0)));
66}
67
68Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id); // from qlocale_win.cpp
69
70/*!
71 \class QWindowsInputContext
72 \brief Windows Input context implementation
73
74 Handles input of foreign characters (particularly East Asian)
75 languages.
76
77 \section1 Testing
78
79 \list
80 \li Install the East Asian language support and choose Japanese (say).
81 \li Compile the \a mainwindows/mdi example and open a text window.
82 \li In the language bar, switch to Japanese and choose the
83 Input method 'Hiragana'.
84 \li In a text editor control, type the syllable \a 'la'.
85 Underlined characters show up, indicating that there is completion
86 available. Press the Space key two times. A completion popup occurs
87 which shows the options.
88 \endlist
89
90 Reconversion: Input texts can be 'converted' into different
91 input modes or more completion suggestions can be made based on
92 context to correct errors. This is bound to the 'Conversion key'
93 (F13-key in Japanese, which can be changed in the
94 configuration). After writing text, pressing the key selects text
95 and triggers a conversion popup, which shows the alternatives for
96 the word.
97
98 \section1 Interaction
99
100 When the user activates input methods, Windows sends
101 WM_IME_STARTCOMPOSITION, WM_IME_COMPOSITION,
102 WM_IME_ENDCOMPOSITION messages that trigger startComposition(),
103 composition(), endComposition(), respectively. No key events are sent.
104
105 composition() determines the markup of the pre-edit or selected
106 text and/or the final text and sends that to the focus object.
107
108 In between startComposition(), endComposition(), multiple
109 compositions may happen (isComposing).
110
111 update() is called to synchronize the position of the candidate
112 window with the microfocus rectangle of the focus object.
113 Also, a hidden caret is moved along with that position,
114 which is important for some Chinese input methods.
115
116 reset() is called to cancel a composition if the mouse is
117 moved outside or for example some Undo/Redo operation is
118 invoked.
119
120 \note Mouse interaction of popups with
121 QtWindows::InputMethodOpenCandidateWindowEvent and
122 QtWindows::InputMethodCloseCandidateWindowEvent
123 needs to be checked (mouse grab might interfere with candidate window).
124
125 \internal
126*/
127
128
130 m_WM_MSIME_MOUSE(RegisterWindowMessage(L"MSIMEMouseOperation")),
133{
134 const quint32 bmpData = 0;
135 m_transparentBitmap = CreateBitmap(2, 2, 1, 1, &bmpData);
136
137 connect(QGuiApplication::inputMethod(), &QInputMethod::cursorRectangleChanged,
138 this, &QWindowsInputContext::cursorRectChanged);
139}
140
142{
143 if (m_transparentBitmap)
144 DeleteObject(m_transparentBitmap);
145}
146
147bool QWindowsInputContext::hasCapability(Capability capability) const
148{
149 switch (capability) {
150 case QPlatformInputContext::HiddenTextCapability:
151 return false; // QTBUG-40691, do not show IME on desktop for password entry fields.
152 default:
153 break;
154 }
155 return true;
156}
157
158/*!
159 \brief Cancels a composition.
160*/
161
163{
164 if (!m_compositionContext.hwnd)
165 return;
166 qCDebug(lcQpaInputMethods) << __FUNCTION__;
167 if (m_compositionContext.isComposing && !m_compositionContext.focusObject.isNull()) {
168 QInputMethodEvent event;
169 if (!m_compositionContext.composition.isEmpty())
170 event.setCommitString(m_compositionContext.composition);
171 QCoreApplication::sendEvent(m_compositionContext.focusObject, &event);
172 endContextComposition();
173 }
174 imeNotifyCancelComposition(m_compositionContext.hwnd);
175 doneContext();
176}
177
179{
180 // ### fixme: On Windows 8.1, it has been observed that the Input context
181 // remains active when this happens resulting in a lock-up. Consecutive
182 // key events still have VK_PROCESSKEY set and are thus ignored.
183 if (m_compositionContext.isComposing)
184 reset();
185 updateEnabled();
186}
187
188HWND QWindowsInputContext::getVirtualKeyboardWindowHandle() const
189{
190 return ::FindWindowA("IPTip_Main_Window", nullptr);
191}
192
194{
195 if (HWND hwnd = getVirtualKeyboardWindowHandle()) {
196 RECT rect;
197 if (::GetWindowRect(hwnd, &rect)) {
198 return QRectF(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
199 }
200 }
201 return QRectF();
202}
203
205{
206 HWND hwnd = getVirtualKeyboardWindowHandle();
207 if (hwnd && ::IsWindowEnabled(hwnd) && ::IsWindowVisible(hwnd))
208 return true;
209 // check if the Input Method Editor is open
210 if (inputMethodAccepted()) {
211 if (QWindow *window = QGuiApplication::focusWindow()) {
212 if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(window)) {
213 if (HIMC himc = ImmGetContext(platformWindow->handle()))
214 return ImmGetOpenStatus(himc);
215 }
216 }
217 }
218 return false;
219}
220
222{
223 if (!inputMethodAccepted())
224 return;
225
226 QWindow *window = QGuiApplication::focusWindow();
227 if (!window)
228 return;
229
230 QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(window);
231 if (!platformWindow)
232 return;
233
234 // Create an invisible 2x2 caret, which will be kept at the microfocus position.
235 // It is important for triggering the on-screen keyboard in touch-screen devices,
236 // for some Chinese input methods, and for Magnifier's "follow keyboard" feature.
237 if (!m_caretCreated && m_transparentBitmap)
238 m_caretCreated = CreateCaret(platformWindow->handle(), m_transparentBitmap, 0, 0);
239
240 if (m_caretCreated) {
241 cursorRectChanged();
242 ShowCaret(platformWindow->handle());
243 }
244}
245
247{
248 if (m_caretCreated) {
249 DestroyCaret();
250 m_caretCreated = false;
251 }
252}
253
254void QWindowsInputContext::updateEnabled()
255{
256 if (!QGuiApplication::focusObject())
257 return;
258 if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(QGuiApplication::focusWindow())) {
259 const bool accepted = inputMethodAccepted();
261 qCDebug(lcQpaInputMethods) << __FUNCTION__ << platformWindow->window() << "accepted=" << accepted;
263 }
264}
265
266void QWindowsInputContext::setWindowsImeEnabled(QWindowsWindow *platformWindow, bool enabled)
267{
268 if (!platformWindow)
269 return;
270 if (enabled) {
271 // Re-enable Windows IME by associating default context.
272 ImmAssociateContextEx(platformWindow->handle(), nullptr, IACE_DEFAULT);
273 } else {
274 // Disable Windows IME by associating 0 context.
275 ImmAssociateContext(platformWindow->handle(), nullptr);
276 }
277}
278
279/*!
280 \brief Moves the candidate window along with microfocus of the focus object.
281*/
282
283void QWindowsInputContext::update(Qt::InputMethodQueries queries)
284{
285 if (queries & Qt::ImEnabled)
286 updateEnabled();
287}
288
289void QWindowsInputContext::cursorRectChanged()
290{
291 QWindow *window = QGuiApplication::focusWindow();
292 if (!window)
293 return;
294
295 qreal factor = QHighDpiScaling::factor(window);
296
297 const QInputMethod *inputMethod = QGuiApplication::inputMethod();
298 const QRectF cursorRectangleF = inputMethod->cursorRectangle();
299 if (!cursorRectangleF.isValid())
300 return;
301
302 const QRect cursorRectangle =
303 QRectF(cursorRectangleF.topLeft() * factor, cursorRectangleF.size() * factor).toRect();
304
305 if (m_caretCreated)
306 SetCaretPos(cursorRectangle.x(), cursorRectangle.y());
307
308 if (!m_compositionContext.hwnd)
309 return;
310
311 qCDebug(lcQpaInputMethods) << __FUNCTION__<< cursorRectangle;
312
313 const HIMC himc = ImmGetContext(m_compositionContext.hwnd);
314 if (!himc)
315 return;
316 // Move candidate list window to the microfocus position.
317 COMPOSITIONFORM cf;
318 // ### need X-like inputStyle config settings
319 cf.dwStyle = CFS_FORCE_POSITION;
320 cf.ptCurrentPos.x = cursorRectangle.x();
321 cf.ptCurrentPos.y = cursorRectangle.y();
322
323 CANDIDATEFORM candf;
324 candf.dwIndex = 0;
325 candf.dwStyle = CFS_EXCLUDE;
326 candf.ptCurrentPos.x = cursorRectangle.x();
327 candf.ptCurrentPos.y = cursorRectangle.y() + cursorRectangle.height();
328 candf.rcArea.left = cursorRectangle.x();
329 candf.rcArea.top = cursorRectangle.y();
330 candf.rcArea.right = cursorRectangle.x() + cursorRectangle.width();
331 candf.rcArea.bottom = cursorRectangle.y() + cursorRectangle.height();
332
333 ImmSetCompositionWindow(himc, &cf);
334 ImmSetCandidateWindow(himc, &candf);
335 ImmReleaseContext(m_compositionContext.hwnd, himc);
336}
337
338void QWindowsInputContext::invokeAction(QInputMethod::Action action, int cursorPosition)
339{
340 if (action != QInputMethod::Click || !m_compositionContext.hwnd) {
341 QPlatformInputContext::invokeAction(action, cursorPosition);
342 return;
343 }
344
345 qCDebug(lcQpaInputMethods) << __FUNCTION__ << cursorPosition << action;
346 if (cursorPosition < 0 || cursorPosition > m_compositionContext.composition.size())
347 reset();
348
349 // Magic code that notifies Japanese IME about the cursor
350 // position.
351 const HIMC himc = ImmGetContext(m_compositionContext.hwnd);
352 const HWND imeWindow = ImmGetDefaultIMEWnd(m_compositionContext.hwnd);
353 const WPARAM mouseOperationCode =
354 MAKELONG(MAKEWORD(MK_LBUTTON, cursorPosition == 0 ? 2 : 1), cursorPosition);
355 SendMessage(imeWindow, m_WM_MSIME_MOUSE, mouseOperationCode, LPARAM(himc));
356 ImmReleaseContext(m_compositionContext.hwnd, himc);
357}
358
359static inline QString getCompositionString(HIMC himc, DWORD dwIndex)
360{
361 enum { bufferSize = 256 };
362 wchar_t buffer[bufferSize];
363 const int length = ImmGetCompositionString(himc, dwIndex, buffer, bufferSize * sizeof(wchar_t));
364 return QString::fromWCharArray(buffer, size_t(length) / sizeof(wchar_t));
365}
366
367// Determine the converted string range as pair of start/length to be selected.
368static inline void getCompositionStringConvertedRange(HIMC himc, int *selStart, int *selLength)
369{
370 enum { bufferSize = 256 };
371 // Find the range of bytes with ATTR_TARGET_CONVERTED set.
372 char attrBuffer[bufferSize];
373 *selStart = *selLength = 0;
374 if (const int attrLength = ImmGetCompositionString(himc, GCS_COMPATTR, attrBuffer, bufferSize)) {
375 int start = 0;
376 while (start < attrLength && !(attrBuffer[start] & ATTR_TARGET_CONVERTED))
377 start++;
378 if (start < attrLength) {
379 int end = start + 1;
380 while (end < attrLength && (attrBuffer[end] & ATTR_TARGET_CONVERTED))
381 end++;
382 *selStart = start;
383 *selLength = end - start;
384 }
385 }
386}
387
392
394{
395 QTextCharFormat result;
396 switch (format) {
397 case PreeditFormat:
398 result.setUnderlineStyle(QTextCharFormat::DashUnderline);
399 break;
400 case SelectionFormat: {
401 // TODO: Should be that of the widget?
402 const QPalette palette = QGuiApplication::palette();
403 const QColor background = palette.text().color();
404 result.setBackground(QBrush(background));
405 result.setForeground(palette.window());
406 break;
407 }
408 }
409 return result;
410}
411
413{
414 QObject *fo = QGuiApplication::focusObject();
415 if (!fo)
416 return false;
417 // This should always match the object.
418 QWindow *window = QGuiApplication::focusWindow();
419 if (!window)
420 return false;
421 qCDebug(lcQpaInputMethods) << __FUNCTION__ << fo << window << "language=" << m_languageId;
422 if (!fo || QWindowsWindow::handleOf(window) != hwnd)
423 return false;
424 initContext(hwnd, fo);
425 startContextComposition();
426 return true;
427}
428
429void QWindowsInputContext::startContextComposition()
430{
431 if (m_compositionContext.isComposing) {
432 qWarning("%s: Called out of sequence.", __FUNCTION__);
433 return;
434 }
435 m_compositionContext.isComposing = true;
436 m_compositionContext.composition.clear();
437 m_compositionContext.position = 0;
438 cursorRectChanged(); // position cursor initially.
439 update(Qt::ImQueryAll);
440}
441
442void QWindowsInputContext::endContextComposition()
443{
444 if (!m_compositionContext.isComposing) {
445 qWarning("%s: Called out of sequence.", __FUNCTION__);
446 return;
447 }
448 m_compositionContext.composition.clear();
449 m_compositionContext.position = 0;
450 m_compositionContext.isComposing = false;
451}
452
453// Create a list of markup attributes for QInputMethodEvent
454// to display the selected part of the intermediate composition
455// result differently.
456static inline QList<QInputMethodEvent::Attribute>
457 intermediateMarkup(int position, int compositionLength,
458 int selStart, int selLength)
459{
460 QList<QInputMethodEvent::Attribute> attributes;
461 if (selStart > 0)
462 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, selStart,
463 standardFormat(PreeditFormat));
464 if (selLength)
465 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selStart, selLength,
466 standardFormat(SelectionFormat));
467 if (selStart + selLength < compositionLength)
468 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selStart + selLength,
469 compositionLength - selStart - selLength,
470 standardFormat(PreeditFormat));
471 if (position >= 0)
472 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, position, selLength ? 0 : 1, QVariant());
473 return attributes;
474}
475
476/*!
477 \brief Notify focus object about markup or final text.
478*/
479
480bool QWindowsInputContext::composition(HWND hwnd, LPARAM lParamIn)
481{
482 const int lParam = int(lParamIn);
483 qCDebug(lcQpaInputMethods) << '>' << __FUNCTION__ << m_compositionContext.focusObject
484 << debugComposition(lParam) << " composing=" << m_compositionContext.isComposing;
485 if (m_compositionContext.focusObject.isNull() || m_compositionContext.hwnd != hwnd || !lParam)
486 return false;
487 const HIMC himc = ImmGetContext(m_compositionContext.hwnd);
488 if (!himc)
489 return false;
490
491 QScopedPointer<QInputMethodEvent> event;
492 if (lParam & (GCS_COMPSTR | GCS_COMPATTR | GCS_CURSORPOS)) {
493 if (!m_compositionContext.isComposing)
494 startContextComposition();
495 // Some intermediate composition result. Parametrize event with
496 // attribute sequence specifying the formatting of the converted part.
497 int selStart, selLength;
498 m_compositionContext.composition = getCompositionString(himc, GCS_COMPSTR);
499 m_compositionContext.position = ImmGetCompositionString(himc, GCS_CURSORPOS, nullptr, 0);
500 getCompositionStringConvertedRange(himc, &selStart, &selLength);
501 if ((lParam & CS_INSERTCHAR) && (lParam & CS_NOMOVECARET)) {
502 // make Korean work correctly. Hope this is correct for all IMEs
503 selStart = 0;
504 selLength = m_compositionContext.composition.size();
505 }
506 if (!selLength)
507 selStart = 0;
508
509 event.reset(new QInputMethodEvent(m_compositionContext.composition,
510 intermediateMarkup(m_compositionContext.position,
511 m_compositionContext.composition.size(),
512 selStart, selLength)));
513 }
514 if (event.isNull())
515 event.reset(new QInputMethodEvent);
516
517 if (lParam & GCS_RESULTSTR) {
518 // A fixed result, return the converted string
519 event->setCommitString(getCompositionString(himc, GCS_RESULTSTR));
520 if (!(lParam & GCS_DELTASTART))
521 endContextComposition();
522 }
523 const bool result = QCoreApplication::sendEvent(m_compositionContext.focusObject, event.data());
524 qCDebug(lcQpaInputMethods) << '<' << __FUNCTION__ << "sending markup="
525 << event->attributes().size() << " commit=" << event->commitString()
526 << " to " << m_compositionContext.focusObject << " returns " << result;
527 update(Qt::ImQueryAll);
528 ImmReleaseContext(m_compositionContext.hwnd, himc);
529 return result;
530}
531
533{
534 qCDebug(lcQpaInputMethods) << __FUNCTION__ << m_endCompositionRecursionGuard << hwnd;
535 // Googles Pinyin Input Method likes to call endComposition again
536 // when we call notifyIME with CPS_CANCEL, so protect ourselves
537 // against that.
538 if (m_endCompositionRecursionGuard || m_compositionContext.hwnd != hwnd)
539 return false;
540 if (m_compositionContext.focusObject.isNull())
541 return false;
542
543 // QTBUG-58300: Ignore WM_IME_ENDCOMPOSITION when CTRL is pressed to prevent
544 // for example the text being cleared when pressing CTRL+A
545 if (m_locale.language() == QLocale::Korean
546 && QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier)) {
547 reset();
548 return true;
549 }
550
551 m_endCompositionRecursionGuard = true;
552
553 imeNotifyCancelComposition(m_compositionContext.hwnd);
554 if (m_compositionContext.isComposing) {
555 QInputMethodEvent event;
556 QCoreApplication::sendEvent(m_compositionContext.focusObject, &event);
557 }
558 doneContext();
559
560 m_endCompositionRecursionGuard = false;
561 return true;
562}
563
564void QWindowsInputContext::initContext(HWND hwnd, QObject *focusObject)
565{
566 if (m_compositionContext.hwnd)
567 doneContext();
568 m_compositionContext.hwnd = hwnd;
569 m_compositionContext.focusObject = focusObject;
570
571 update(Qt::ImQueryAll);
572 m_compositionContext.isComposing = false;
573 m_compositionContext.position = 0;
574}
575
576void QWindowsInputContext::doneContext()
577{
578 if (!m_compositionContext.hwnd)
579 return;
580 m_compositionContext.hwnd = nullptr;
581 m_compositionContext.composition.clear();
582 m_compositionContext.position = 0;
583 m_compositionContext.isComposing = false;
584 m_compositionContext.focusObject = nullptr;
585}
586
588 LPARAM lParam,
589 LRESULT *result)
590{
591 switch (int(wParam)) {
592 case IMR_RECONVERTSTRING: {
593 const int size = reconvertString(reinterpret_cast<RECONVERTSTRING *>(lParam));
594 if (size < 0)
595 return false;
596 *result = size;
597 }
598 return true;
599 case IMR_CONFIRMRECONVERTSTRING:
600 return true;
601 default:
602 break;
603 }
604 return false;
605}
606
607void QWindowsInputContext::handleInputLanguageChanged(WPARAM wparam, LPARAM lparam)
608{
609 const LCID newLanguageId = languageIdFromLocaleId(WORD(lparam));
610 if (newLanguageId == m_languageId)
611 return;
612 const LCID oldLanguageId = m_languageId;
613 m_languageId = newLanguageId;
614 m_locale = qt_localeFromLCID(m_languageId);
615 emitLocaleChanged();
616
617 qCDebug(lcQpaInputMethods) << __FUNCTION__ << Qt::hex << Qt::showbase
618 << oldLanguageId << "->" << newLanguageId << "Character set:"
619 << DWORD(wparam) << Qt::dec << Qt::noshowbase << m_locale;
620}
621
622/*!
623 \brief Determines the string for reconversion with selection.
624
625 This is triggered twice by WM_IME_REQUEST, first with reconv=0
626 to determine the length and later with a reconv struct to obtain
627 the string with the position of the selection to be reconverted.
628
629 Obtains the text from the focus object and marks the word
630 for selection (might not be entirely correct for Japanese).
631*/
632
633int QWindowsInputContext::reconvertString(RECONVERTSTRING *reconv)
634{
635 QObject *fo = QGuiApplication::focusObject();
636 if (!fo)
637 return false;
638
639 const QVariant surroundingTextV = QInputMethod::queryFocusObject(Qt::ImSurroundingText, QVariant());
640 if (!surroundingTextV.isValid())
641 return -1;
642 const QString surroundingText = surroundingTextV.toString();
643 const int memSize = int(sizeof(RECONVERTSTRING))
644 + (surroundingText.length() + 1) * int(sizeof(ushort));
645 qCDebug(lcQpaInputMethods) << __FUNCTION__ << " reconv=" << reconv
646 << " surroundingText=" << surroundingText << " size=" << memSize;
647 // If memory is not allocated, return the required size.
648 if (!reconv)
649 return surroundingText.isEmpty() ? -1 : memSize;
650
651 const QVariant posV = QInputMethod::queryFocusObject(Qt::ImCursorPosition, QVariant());
652 const int pos = posV.isValid() ? posV.toInt() : 0;
653 // Find the word in the surrounding text.
654 QTextBoundaryFinder bounds(QTextBoundaryFinder::Word, surroundingText);
655 bounds.setPosition(pos);
656 if (bounds.position() > 0 && !(bounds.boundaryReasons() & QTextBoundaryFinder::StartOfItem))
657 bounds.toPreviousBoundary();
658 const int startPos = bounds.position();
659 bounds.toNextBoundary();
660 const int endPos = bounds.position();
661 qCDebug(lcQpaInputMethods) << __FUNCTION__ << " boundary=" << startPos << endPos;
662 // Select the text, this will be overwritten by following IME events.
663 QList<QInputMethodEvent::Attribute> attributes;
664 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, startPos, endPos-startPos, QVariant());
665 QInputMethodEvent selectEvent(QString(), attributes);
666 QCoreApplication::sendEvent(fo, &selectEvent);
667
668 reconv->dwSize = DWORD(memSize);
669 reconv->dwVersion = 0;
670
671 reconv->dwStrLen = DWORD(surroundingText.size());
672 reconv->dwStrOffset = sizeof(RECONVERTSTRING);
673 reconv->dwCompStrLen = DWORD(endPos - startPos); // TCHAR count.
674 reconv->dwCompStrOffset = DWORD(startPos) * sizeof(ushort); // byte count.
675 reconv->dwTargetStrLen = reconv->dwCompStrLen;
676 reconv->dwTargetStrOffset = reconv->dwCompStrOffset;
677 auto *pastReconv = reinterpret_cast<ushort *>(reconv + 1);
678 std::copy(surroundingText.utf16(), surroundingText.utf16() + surroundingText.size(),
679 pastReconv);
680 return memSize;
681}
682
683QT_END_NAMESPACE
Singleton container for all relevant information.
Windows Input context implementation.
QRectF keyboardRect() const override
This function can be reimplemented to return virtual keyboard rectangle in currently active window co...
void update(Qt::InputMethodQueries) override
Moves the candidate window along with microfocus of the focus object.
bool composition(HWND hwnd, LPARAM lParam)
Notify focus object about markup or final text.
bool hasCapability(Capability capability) const override
Returns whether the implementation supports capability.
void invokeAction(QInputMethod::Action, int cursorPosition) override
Called when the word currently being composed in the input item is tapped by the user.
void handleInputLanguageChanged(WPARAM wparam, LPARAM lparam)
void showInputPanel() override
Request to show input panel.
static void setWindowsImeEnabled(QWindowsWindow *platformWindow, bool enabled)
bool isInputPanelVisible() const override
Returns input panel visibility status.
void setFocusObject(QObject *object) override
This virtual method gets called to notify updated focus to object.
bool handleIME_Request(WPARAM wparam, LPARAM lparam, LRESULT *result)
int reconvertString(RECONVERTSTRING *reconv)
Determines the string for reconversion with selection.
void hideInputPanel() override
Request to hide input panel.
void reset() override
Cancels a composition.
Raster or OpenGL Window.
static QList< QInputMethodEvent::Attribute > intermediateMarkup(int position, int compositionLength, int selStart, int selLength)
static void imeNotifyCancelComposition(HWND hwnd)
static void getCompositionStringConvertedRange(HIMC himc, int *selStart, int *selLength)
static QString getCompositionString(HIMC himc, DWORD dwIndex)
static LCID currentInputLanguageId()
static QTextFormat standardFormat(StandardFormat format)
static LCID languageIdFromLocaleId(LCID localeId)