macOS: Implement NSServicesMenuRequestor protocol for Writing Tools support
The AI based Writing Tools in macOS 15 uses the NSServicesMenuRequestor protocol to interact with custom NSView. By implementing the protocol we also gain support for service menu items such as "Look Up in Dictionary" or "Make New Sticky Note". Note that we only support plain text for now, 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. The Writing Tools feature itself is only available on macOS 15.1. To trigger it a native context menu with edit actions has to be shown, meaning this will not work out of the box for Qt Widgets. For Qt Quick with popupType set to Popup.Native the menu item is added as expected. Task-number: QTBUG-126238 Change-Id: I2cd4aa9af8d613c7c67b3c19a70a23660dde2154 Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io> (cherry picked from commit 888429a734bf379ee59519b5d6047561df66c9a5) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
ece128fc5e
commit
987b215f08
@ -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;
|
||||
|
@ -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
|
||||
|
@ -82,6 +82,9 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSViewMouseMoveHelper);
|
||||
@property (readonly) QObject* focusObject;
|
||||
@end
|
||||
|
||||
@interface QNSView (ServicesMenu) <NSServicesMenuRequestor>
|
||||
@end
|
||||
|
||||
@interface QT_MANGLE_NAMESPACE(QNSViewMenuHelper) : NSObject
|
||||
- (instancetype)initWithView:(QNSView *)theView;
|
||||
@end
|
||||
|
@ -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<NSPasteboardType> *)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
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user