macOS: Use helper method for querying input method parameters

Pick-to: 6.2
Change-Id: I379d4ca20101899db2dfbd8f0c5a22f423e40d6b
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
bb10
Tor Arne Vestbø 2021-08-17 14:56:15 +02:00
parent 60ca8ca5bc
commit a5571ec8a0
4 changed files with 101 additions and 105 deletions

View File

@ -361,6 +361,15 @@ QSendSuperHelper<Args...> qt_objcDynamicSuperHelper(id receiver, SEL selector, A
// -------------------------------------------------------------------------
struct InputMethodQueryResult : public QHash<Qt::InputMethodQuery, QVariant>
{
operator bool() { return !isEmpty(); }
};
InputMethodQueryResult queryInputMethod(QObject *object, Qt::InputMethodQueries queries = Qt::ImEnabled);
// -------------------------------------------------------------------------
QDebug operator<<(QDebug, const NSRange &);
QDebug operator<<(QDebug, SEL);

View File

@ -511,6 +511,28 @@ void q_IOObjectRelease(io_object_t obj)
// -------------------------------------------------------------------------
InputMethodQueryResult queryInputMethod(QObject *object, Qt::InputMethodQueries queries)
{
if (object) {
QInputMethodQueryEvent queryEvent(queries | Qt::ImEnabled);
if (QCoreApplication::sendEvent(object, &queryEvent)) {
if (queryEvent.value(Qt::ImEnabled).toBool()) {
InputMethodQueryResult result;
static QMetaEnum queryEnum = QMetaEnum::fromType<Qt::InputMethodQuery>();
for (int i = 0; i < queryEnum.keyCount(); ++i) {
auto query = Qt::InputMethodQuery(queryEnum.value(i));
if (queries & query)
result.insert(query, queryEvent.value(query));
}
return result;
}
}
}
return {};
}
// -------------------------------------------------------------------------
QDebug operator<<(QDebug debug, const NSRange &range)
{
if (range.location == NSNotFound) {

View File

@ -49,14 +49,9 @@
qCDebug(lcQpaKeys) << "Canceling composition" << m_composingText
<< "for focus object" << m_composingFocusObject;
if (m_composingFocusObject) {
QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
if (QCoreApplication::sendEvent(m_composingFocusObject, &queryEvent)) {
if (queryEvent.value(Qt::ImEnabled).toBool()) {
QInputMethodEvent e;
QCoreApplication::sendEvent(m_composingFocusObject, &e);
}
}
if (queryInputMethod(m_composingFocusObject)) {
QInputMethodEvent e;
QCoreApplication::sendEvent(m_composingFocusObject, &e);
}
m_composingText.clear();
@ -71,17 +66,14 @@
<< "for focus object" << m_composingFocusObject;
if (!m_composingText.isEmpty()) {
if (QObject *fo = m_platformWindow->window()->focusObject()) {
QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
if (QCoreApplication::sendEvent(fo, &queryEvent)) {
if (queryEvent.value(Qt::ImEnabled).toBool()) {
QInputMethodEvent e;
e.setCommitString(m_composingText);
QCoreApplication::sendEvent(fo, &e);
}
}
QObject *focusObject = m_platformWindow->window()->focusObject();
if (queryInputMethod(focusObject)) {
QInputMethodEvent e;
e.setCommitString(m_composingText);
QCoreApplication::sendEvent(focusObject, &e);
}
}
m_composingText.clear();
m_composingFocusObject = nullptr;
}
@ -121,17 +113,14 @@
commitString = QString::fromCFString(reinterpret_cast<CFStringRef>(aString));
};
}
if (QObject *fo = m_platformWindow->window()->focusObject()) {
QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
if (QCoreApplication::sendEvent(fo, &queryEvent)) {
if (queryEvent.value(Qt::ImEnabled).toBool()) {
QInputMethodEvent e;
e.setCommitString(commitString);
QCoreApplication::sendEvent(fo, &e);
// prevent handleKeyEvent from sending a key event
m_sendKeyEvent = false;
}
}
QObject *focusObject = m_platformWindow->window()->focusObject();
if (queryInputMethod(focusObject)) {
QInputMethodEvent e;
e.setCommitString(commitString);
QCoreApplication::sendEvent(focusObject, &e);
// prevent handleKeyEvent from sending a key event
m_sendKeyEvent = false;
}
m_composingText.clear();
@ -192,16 +181,13 @@
m_composingText = preeditString;
if (QObject *fo = m_platformWindow->window()->focusObject()) {
m_composingFocusObject = fo;
QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
if (QCoreApplication::sendEvent(fo, &queryEvent)) {
if (queryEvent.value(Qt::ImEnabled).toBool()) {
QInputMethodEvent e(preeditString, attrs);
QCoreApplication::sendEvent(fo, &e);
// prevent handleKeyEvent from sending a key event
m_sendKeyEvent = false;
}
if (QObject *focusObject = m_platformWindow->window()->focusObject()) {
m_composingFocusObject = focusObject;
if (queryInputMethod(focusObject)) {
QInputMethodEvent e(preeditString, attrs);
QCoreApplication::sendEvent(focusObject, &e);
// prevent handleKeyEvent from sending a key event
m_sendKeyEvent = false;
}
}
}
@ -214,22 +200,19 @@
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
{
Q_UNUSED(actualRange);
QObject *fo = m_platformWindow->window()->focusObject();
if (!fo)
return nil;
QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection);
if (!QCoreApplication::sendEvent(fo, &queryEvent))
return nil;
if (!queryEvent.value(Qt::ImEnabled).toBool())
return nil;
QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
if (selectedText.isEmpty())
return nil;
QObject *focusObject = m_platformWindow->window()->focusObject();
if (auto queryResult = queryInputMethod(focusObject, Qt::ImCurrentSelection)) {
QString selectedText = queryResult.value(Qt::ImCurrentSelection).toString();
if (selectedText.isEmpty())
return nil;
QCFString string(selectedText.mid(aRange.location, aRange.length));
const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string);
return [[[NSAttributedString alloc] initWithString:const_cast<NSString *>(tmpString)] autorelease];
QCFString string(selectedText.mid(aRange.location, aRange.length));
const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string);
return [[[NSAttributedString alloc] initWithString:const_cast<NSString *>(tmpString)] autorelease];
} else {
return nil;
}
}
- (NSRange)markedRange
@ -247,24 +230,13 @@
- (NSRange)selectedRange
{
NSRange selectedRange = {0, 0};
QObject *fo = m_platformWindow->window()->focusObject();
if (!fo)
return selectedRange;
QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection);
if (!QCoreApplication::sendEvent(fo, &queryEvent))
return selectedRange;
if (!queryEvent.value(Qt::ImEnabled).toBool())
return selectedRange;
QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
if (!selectedText.isEmpty()) {
selectedRange.location = 0;
selectedRange.length = selectedText.length();
QObject *focusObject = m_platformWindow->window()->focusObject();
if (auto queryResult = queryInputMethod(focusObject, Qt::ImCurrentSelection)) {
QString selectedText = queryResult.value(Qt::ImCurrentSelection).toString();
return selectedText.isEmpty() ? NSMakeRange(0, 0) : NSMakeRange(0, selectedText.length());
} else {
return NSMakeRange(0, 0);
}
return selectedRange;
}
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
@ -272,20 +244,14 @@
Q_UNUSED(aRange);
Q_UNUSED(actualRange);
QObject *fo = m_platformWindow->window()->focusObject();
if (!fo)
QWindow *window = m_platformWindow->window();
if (queryInputMethod(window->focusObject())) {
QRect cursorRect = qApp->inputMethod()->cursorRectangle().toRect();
cursorRect.moveBottomLeft(window->mapToGlobal(cursorRect.bottomLeft()));
return QCocoaScreen::mapToNative(cursorRect);
} else {
return NSZeroRect;
QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
if (!QCoreApplication::sendEvent(fo, &queryEvent))
return NSZeroRect;
if (!queryEvent.value(Qt::ImEnabled).toBool())
return NSZeroRect;
// The returned rect is always based on the internal cursor.
QRect mr = qApp->inputMethod()->cursorRectangle().toRect();
mr.moveBottomLeft(m_platformWindow->window()->mapToGlobal(mr.bottomLeft()));
return QCocoaScreen::mapToNative(mr);
}
}
- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint
@ -303,18 +269,10 @@
if (m_platformWindow->window() != QGuiApplication::focusWindow())
return nil;
QObject *fo = m_platformWindow->window()->focusObject();
if (!fo)
if (queryInputMethod(m_platformWindow->window()->focusObject()))
return @[NSUnderlineColorAttributeName, NSUnderlineStyleAttributeName];
else
return nil;
QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
if (!QCoreApplication::sendEvent(fo, &queryEvent))
return nil;
if (!queryEvent.value(Qt::ImEnabled).toBool())
return nil;
// Support only underline color/style.
return @[NSUnderlineColorAttributeName, NSUnderlineStyleAttributeName];
}
- (void)textInputContextKeyboardSelectionDidChangeNotification:(NSNotification *)textInputContextKeyboardSelectionDidChangeNotification

View File

@ -113,32 +113,39 @@
}
}
QObject *fo = m_platformWindow->window()->focusObject();
if (m_sendKeyEvent && fo) {
QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImHints);
if (QCoreApplication::sendEvent(fo, &queryEvent)) {
bool imEnabled = queryEvent.value(Qt::ImEnabled).toBool();
Qt::InputMethodHints hints = static_cast<Qt::InputMethodHints>(queryEvent.value(Qt::ImHints).toUInt());
// make sure we send dead keys and the next key to the input method for composition
QObject *focusObject = m_platformWindow->window()->focusObject();
if (m_sendKeyEvent && focusObject) {
if (auto queryResult = queryInputMethod(focusObject, Qt::ImHints)) {
auto hints = static_cast<Qt::InputMethodHints>(queryResult.value(Qt::ImHints).toUInt());
// Make sure we send dead keys and the next key to the input method for composition
const bool ignoreHidden = (hints & Qt::ImhHiddenText) && !text.isEmpty() && !m_lastKeyDead;
if (imEnabled && !(hints & Qt::ImhDigitsOnly || hints & Qt::ImhFormattedNumbersOnly || ignoreHidden)) {
// pass the key event to the input method. note that m_sendKeyEvent may be set to false during this call
qCDebug(lcQpaKeys) << "Interpreting key event for focus object" << fo;
if (!(hints & Qt::ImhDigitsOnly || hints & Qt::ImhFormattedNumbersOnly || ignoreHidden)) {
// Pass the key event to the input method. Note that m_sendKeyEvent may be set
// to false during this call
qCDebug(lcQpaKeys) << "Interpreting key event for focus object" << focusObject;
m_currentlyInterpretedKeyEvent = nsevent;
[self interpretKeyEvents:@[nsevent]];
// If the receiver opens an editor in response to a key press, then the focus will change, the input
// method will be reset, and the first key press will be gone. If the focus object changes, then we
// need to pass the key event to the input method once more.
if (qApp->focusObject() != fo) {
if (qApp->focusObject() != focusObject) {
qCDebug(lcQpaKeys) << "Interpreting key event again for new focus object" << qApp->focusObject();
[self interpretKeyEvents:@[nsevent]];
}
m_currentlyInterpretedKeyEvent = 0;
// if the last key we sent was dead, then pass the next key to the IM as well to complete composition
// If the last key we sent was dead, then pass the next
// key to the IM as well to complete composition.
m_lastKeyDead = text.isEmpty();
}
}
}
if (m_resendKeyEvent)
m_sendKeyEvent = true;
}