diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2021-08-27 15:55:39 +0200 |
---|---|---|
committer | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2021-08-27 22:41:06 +0200 |
commit | 3de396590cb41ddf05f91dfb1d2db8c1fe34ff56 (patch) | |
tree | dad32fb77b5c3b1b3efd343a8c4368b598e61b2d /src/plugins/platforms/cocoa | |
parent | 9e1875483ceaf907226f84cd6a58ab59f7f16f80 (diff) |
macOS: Correctly implement attributedSubstringForProposedRange
The substring range refers to the entire text of the focus object,
not just the selection.
As there is no way to pull out the entire text via input method queries
we do the best we can via ImTextBeforeCursor and ImTextAfterCursor.
Returning the correct substring enables input method features such
as backtracking into already committed text with the Hiragana IM,
as well as the Keyboard Viewer's 'Current Text' toolbar.
Pick-to: 6.2
Change-Id: I53286ef1e8e7c5fba37858dda7317ae74d95b528
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Diffstat (limited to 'src/plugins/platforms/cocoa')
-rw-r--r-- | src/plugins/platforms/cocoa/qnsview_complextext.mm | 42 |
1 files changed, 36 insertions, 6 deletions
diff --git a/src/plugins/platforms/cocoa/qnsview_complextext.mm b/src/plugins/platforms/cocoa/qnsview_complextext.mm index 3fcc0f23fa..05c0418ee7 100644 --- a/src/plugins/platforms/cocoa/qnsview_complextext.mm +++ b/src/plugins/platforms/cocoa/qnsview_complextext.mm @@ -435,17 +435,47 @@ } } +/* + Returns an attributed string derived from the given range + in the underlying focus object's text storage. + + Input methods may call this with a proposed range that is + out of bounds. For example, the InkWell text input service + may ask for the contents of the text input client that extends + beyond the document's range. To remedy this we always compute + the intersection between the proposed range and the available + text. + + If the intersection is completely outside of the available text + this method returns nil. +*/ - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange { - Q_UNUSED(actualRange); - QObject *focusObject = m_platformWindow->window()->focusObject(); - if (auto queryResult = queryInputMethod(focusObject, Qt::ImCurrentSelection)) { - QString selectedText = queryResult.value(Qt::ImCurrentSelection).toString(); - if (selectedText.isEmpty()) + if (auto queryResult = queryInputMethod(focusObject, + Qt::ImAbsolutePosition | Qt::ImTextBeforeCursor | Qt::ImTextAfterCursor)) { + const int absoluteCursorPosition = queryResult.value(Qt::ImAbsolutePosition).toInt(); + const QString textBeforeCursor = queryResult.value(Qt::ImTextBeforeCursor).toString(); + const QString textAfterCursor = queryResult.value(Qt::ImTextAfterCursor).toString(); + + // The documentation doesn't say whether the marked text should be included + // in the available text, but observing NSTextView shows that this is the + // case, so we follow suit. + const QString availableText = textBeforeCursor + m_composingText + textAfterCursor; + const NSRange availableRange = NSMakeRange(absoluteCursorPosition - textBeforeCursor.length(), + availableText.length()); + + const NSRange intersectedRange = NSIntersectionRange(range, availableRange); + if (actualRange) + *actualRange = intersectedRange; + + if (!intersectedRange.length) return nil; - NSString *substring = QStringView(selectedText).mid(range.location, range.length).toNSString(); + NSString *substring = QStringView(availableText).mid( + intersectedRange.location - availableRange.location, + intersectedRange.length).toNSString(); + return [[[NSAttributedString alloc] initWithString:substring] autorelease]; } else { |