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
parent
60ca8ca5bc
commit
a5571ec8a0
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue