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
qcocoainputcontext.mm
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
5#include <AppKit/AppKit.h>
6
7#include "qnsview.h"
10#include "qcocoawindow.h"
11#include "qcocoahelpers.h"
12
13#include <Carbon/Carbon.h>
14
15#include <QtCore/QRect>
16#include <QtGui/QGuiApplication>
17#include <QtGui/QWindow>
18
19QT_BEGIN_NAMESPACE
20
21static QNSView *inputViewForWindow(QWindow *focusWindow)
22{
23 if (!focusWindow)
24 return nil;
25
26 auto *platformWindow = focusWindow->handle();
27 if (!platformWindow)
28 return nil;
29
30 auto *cocoaWindow = static_cast<QCocoaWindow *>(platformWindow);
31 // AppKit sends setMarkedText to the first responder, so a child QNSView
32 // can own the active composition. Prefer that view, but fall back to the
33 // platform window's view if the first responder is not a QNSView.
34 NSView *firstResponder = qt_objc_cast<NSView *>(cocoaWindow->nativeWindow().firstResponder);
35 if (QNSView *view = qnsview_cast(firstResponder))
36 return view;
37 return qnsview_cast(cocoaWindow->view());
38}
39
40/*!
41 \class QCocoaInputContext
42 \brief Cocoa Input context implementation
43
44 Handles input of languages that support character composition,
45 for example East Asian languages.
46
47 \section1 Testing
48
49 \list
50 \o Select input sources like 'Kotoeri' in Language & Text Preferences
51 \o Compile the \a mainwindows/mdi example and open a text window.
52 \o In the language bar, switch to 'Hiragana'.
53 \o In a text editor control, type the syllable \a 'la'.
54 Underlined characters show up, indicating that there is completion
55 available. Press the Space key two times. A completion popup occurs
56 which shows the options.
57 \endlist
58
59 \section1 Interaction
60
61 Input method support in Cocoa is based on the NSTextInputClient protocol,
62 therefore almost all functionality is in QNSView (qnsview_complextext.mm).
63*/
64
65QCocoaInputContext::QCocoaInputContext()
66 : QPlatformInputContext()
67 , m_focusWindow(QGuiApplication::focusWindow())
68{
69 m_inputSourceObserver = QMacNotificationObserver(nil,
70 NSTextInputContextKeyboardSelectionDidChangeNotification, [&]() {
71 qCDebug(lcQpaInputMethods) << "Text input source changed";
72 updateLocale();
73 });
74
75 updateLocale();
76}
77
78QCocoaInputContext::~QCocoaInputContext()
79{
80}
81
82/*!
83 Commits the current composition if there is one,
84 by "unmarking" the text in the edit buffer, and
85 informing the system input context of this fact.
86*/
87void QCocoaInputContext::commit()
88{
89 qCDebug(lcQpaInputMethods) << "Committing composition";
90
91 QNSView *view = inputViewForWindow(m_focusWindow);
92 if (!view)
93 return;
94
95 [view unmarkText];
96
97 [view.inputContext discardMarkedText];
98 if (view.inputContext != NSTextInputContext.currentInputContext) {
99 // discardMarkedText will activate the TSM document of the given input context,
100 // which may not match the current input context. To ensure the current input
101 // context is not left in an inconsistent state with a deactivated document
102 // we need to manually activate it here.
103 [NSTextInputContext.currentInputContext activate];
104 }
105}
106
107
108/*!
109 \brief Cancels a composition.
110*/
111void QCocoaInputContext::reset()
112{
113 qCDebug(lcQpaInputMethods) << "Resetting input method";
114
115 QNSView *view = inputViewForWindow(m_focusWindow);
116 if (!view)
117 return;
118
119 if (NSTextInputContext *ctxt = [NSTextInputContext currentInputContext]) {
120 [ctxt discardMarkedText];
121 [view unmarkText];
122 }
123}
124
125void QCocoaInputContext::setFocusObject(QObject *focusObject)
126{
127 qCDebug(lcQpaInputMethods) << "Focus object changed to" << focusObject;
128
129 if (m_focusWindow == QGuiApplication::focusWindow()) {
130 if (!m_focusWindow)
131 return;
132
133 QNSView *view = inputViewForWindow(m_focusWindow);
134 if (!view)
135 return;
136
137 if (NSTextInputContext *ctxt = [NSTextInputContext currentInputContext]) {
138 [ctxt discardMarkedText];
139 [view cancelComposingText];
140 }
141 } else {
142 m_focusWindow = QGuiApplication::focusWindow();
143 }
144}
145
146void QCocoaInputContext::updateLocale()
147{
148 QCFType<TISInputSourceRef> source = TISCopyCurrentKeyboardInputSource();
149 NSArray *languages = static_cast<NSArray*>(TISGetInputSourceProperty(source,
150 kTISPropertyInputSourceLanguages));
151
152 qCDebug(lcQpaInputMethods) << "Input source supports" << languages;
153 if (!languages.count)
154 return;
155
156 QString language = QString::fromNSString(languages.firstObject);
157 QLocale locale(language);
158
159 bool localeUpdated = m_locale != locale;
160 static bool firstUpdate = true;
161
162 m_locale = locale;
163
164 if (localeUpdated && !firstUpdate) {
165 qCDebug(lcQpaInputMethods) << "Reporting new locale" << locale;
166 emitLocaleChanged();
167 }
168
169 firstUpdate = false;
170}
171
172QT_END_NAMESPACE