From a5571ec8a0eee6a19d4533113f9ef2e74540cfe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Tue, 17 Aug 2021 14:56:15 +0200 Subject: macOS: Use helper method for querying input method parameters Pick-to: 6.2 Change-Id: I379d4ca20101899db2dfbd8f0c5a22f423e40d6b Reviewed-by: Volker Hilsheimer --- src/plugins/platforms/cocoa/qcocoahelpers.h | 9 ++ src/plugins/platforms/cocoa/qcocoahelpers.mm | 22 ++++ src/plugins/platforms/cocoa/qnsview_complextext.mm | 144 ++++++++------------- src/plugins/platforms/cocoa/qnsview_keys.mm | 31 +++-- 4 files changed, 101 insertions(+), 105 deletions(-) (limited to 'src') diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index 713b19dad5..f97521fe02 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -361,6 +361,15 @@ QSendSuperHelper qt_objcDynamicSuperHelper(id receiver, SEL selector, A // ------------------------------------------------------------------------- +struct InputMethodQueryResult : public QHash +{ + operator bool() { return !isEmpty(); } +}; + +InputMethodQueryResult queryInputMethod(QObject *object, Qt::InputMethodQueries queries = Qt::ImEnabled); + +// ------------------------------------------------------------------------- + QDebug operator<<(QDebug, const NSRange &); QDebug operator<<(QDebug, SEL); diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 723cfcb952..102cf28452 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -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(); + 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) { diff --git a/src/plugins/platforms/cocoa/qnsview_complextext.mm b/src/plugins/platforms/cocoa/qnsview_complextext.mm index fb19270017..1e5423ab0e 100644 --- a/src/plugins/platforms/cocoa/qnsview_complextext.mm +++ b/src/plugins/platforms/cocoa/qnsview_complextext.mm @@ -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(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((CFStringRef)string); - return [[[NSAttributedString alloc] initWithString:const_cast(tmpString)] autorelease]; + QCFString string(selectedText.mid(aRange.location, aRange.length)); + const NSString *tmpString = reinterpret_cast((CFStringRef)string); + return [[[NSAttributedString alloc] initWithString:const_cast(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) - return NSZeroRect; - - QInputMethodQueryEvent queryEvent(Qt::ImEnabled); - if (!QCoreApplication::sendEvent(fo, &queryEvent)) - return NSZeroRect; - if (!queryEvent.value(Qt::ImEnabled).toBool()) + 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; - - // 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) - return nil; - - QInputMethodQueryEvent queryEvent(Qt::ImEnabled); - if (!QCoreApplication::sendEvent(fo, &queryEvent)) - return nil; - if (!queryEvent.value(Qt::ImEnabled).toBool()) + if (queryInputMethod(m_platformWindow->window()->focusObject())) + return @[NSUnderlineColorAttributeName, NSUnderlineStyleAttributeName]; + else return nil; - - // Support only underline color/style. - return @[NSUnderlineColorAttributeName, NSUnderlineStyleAttributeName]; } - (void)textInputContextKeyboardSelectionDidChangeNotification:(NSNotification *)textInputContextKeyboardSelectionDidChangeNotification diff --git a/src/plugins/platforms/cocoa/qnsview_keys.mm b/src/plugins/platforms/cocoa/qnsview_keys.mm index 1975ac5f3a..3d339fe541 100644 --- a/src/plugins/platforms/cocoa/qnsview_keys.mm +++ b/src/plugins/platforms/cocoa/qnsview_keys.mm @@ -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(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(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; } -- cgit v1.2.3