diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index c6862a9e659..0e5f8212e74 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -43,6 +43,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaClipboard) Q_DECLARE_LOGGING_CATEGORY(lcInputDevices) Q_DECLARE_LOGGING_CATEGORY(lcQpaDialogs) Q_DECLARE_LOGGING_CATEGORY(lcQpaMenus) +Q_DECLARE_LOGGING_CATEGORY(lcQpaServices) class QPixmap; class QString; diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 1eba88d5e33..67f2f9fb094 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -30,6 +30,7 @@ Q_LOGGING_CATEGORY(lcQpaClipboard, "qt.qpa.clipboard") Q_LOGGING_CATEGORY(lcInputDevices, "qt.qpa.input.devices") Q_LOGGING_CATEGORY(lcQpaDialogs, "qt.qpa.dialogs") Q_LOGGING_CATEGORY(lcQpaMenus, "qt.qpa.menus") +Q_LOGGING_CATEGORY(lcQpaServices, "qt.qpa.services") // // Conversion Functions diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index eb998b04099..7289c66ea65 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -82,6 +82,9 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSViewMouseMoveHelper); @property (readonly) QObject* focusObject; @end +@interface QNSView (ServicesMenu) +@end + @interface QT_MANGLE_NAMESPACE(QNSViewMenuHelper) : NSObject - (instancetype)initWithView:(QNSView *)theView; @end diff --git a/src/plugins/platforms/cocoa/qnsview_complextext.mm b/src/plugins/platforms/cocoa/qnsview_complextext.mm index d7f8f4baf09..cb512d5893d 100644 --- a/src/plugins/platforms/cocoa/qnsview_complextext.mm +++ b/src/plugins/platforms/cocoa/qnsview_complextext.mm @@ -598,3 +598,65 @@ } @end + +@implementation QNSView (ServicesMenu) + +// Support for reading and writing from service menu pasteboards, which is also +// how the writing tools interact with custom NSView. Note that we only support +// plain text, which means that a rich text selection will lose all its styling +// when fed through a service that changes the text. To support rich text we +// need IM plumbing that operates on QMimeData. + +- (id)validRequestorForSendType:(NSPasteboardType)sendType returnType:(NSPasteboardType)returnType +{ + bool canWriteToPasteboard = [&]{ + if (![sendType isEqualToString:NSPasteboardTypeString]) + return false; + if (auto queryResult = queryInputMethod(self.focusObject, Qt::ImCurrentSelection)) { + auto selectedText = queryResult.value(Qt::ImCurrentSelection).toString(); + if (!selectedText.isEmpty()) + return true; + } + return false; + }(); + + bool canReadFromPastboard = [returnType isEqualToString:NSPasteboardTypeString]; + + if ((sendType && !canWriteToPasteboard) || (returnType && !canReadFromPastboard)) { + return [super validRequestorForSendType:sendType returnType:returnType]; + } else { + qCDebug(lcQpaServices) << "Accepting service interaction for send" << sendType << "and receive" << returnType; + return self; + } +} + +- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types +{ + if ([types containsObject:NSPasteboardTypeString] + // Check for the deprecated NSStringPboardType as well, as even if we + // claim to only support NSPasteboardTypeString, we get callbacks for + // the deprecated type. + || QT_IGNORE_DEPRECATIONS([types containsObject:NSStringPboardType])) { + if (auto queryResult = queryInputMethod(self.focusObject, Qt::ImCurrentSelection)) { + auto selectedText = queryResult.value(Qt::ImCurrentSelection).toString(); + qCDebug(lcQpaServices) << "Writing" << selectedText << "to service pasteboard" << pasteboard.name; + return [pasteboard writeObjects:@[ selectedText.toNSString() ]]; + } + } + return NO; +} + +- (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard +{ + NSString *insertedString = [pasteboard stringForType:NSPasteboardTypeString]; + if (!insertedString) + return NO; + + qCDebug(lcQpaServices) << "Reading" << insertedString << "from service pasteboard" << pasteboard.name; + [self insertText:insertedString replacementRange:{NSNotFound, 0}]; + return YES; +} + +@end + +