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