How to use Share API in macOS and Qt Quick
This article may require cleanup to meet the Qt Wiki's quality standards. Reason: Auto-imported from ExpressionEngine. Please improve this article if you can. Remove the {{cleanup}} tag and add this page to Updated pages list after it's clean. |
Hi everyone!
In this article I've tried to explain how to use macOS Share API in your Qt Quick app. Share API was implemented in OS X Yosemite (v10.10).
As you know all frameworks in macOS which you could use in your application created with Objective-C/C+. That's why you can't use framework in your C+ classes. You need to rename your .cpp file to .mm and then you can call Objective-C code. But sometimes you need to call methods with arguments with Objective-C. As you know in .h file you can't call Objective-C code. You must create Objective-C class which will call all needed methods to work with target framework.
First you need to create class with all methods in Objective-C/C++ to use Share API. To do this you need follow a few steps:
- Create a new class and rename .cpp file to .mm.
- Add .h file to .pro of your project in section "OBJECTIVE_HEADERS" and .mm to "OBJECTIVE_SOURCES".
- Change .h file content to this code:
#ifdef __cplusplus
extern "C" {
#endif
#import <Foundation/Foundation.h>
#import <Social/Social.h>
/**
typedef QfSharePickerItemClicked *
param sharingService Selected type of sharing.
*
author Andrew Shapovelov*/
typedef void(QfSharePickerItemClicked)(NSSharingService* sharingService);
/**
class QfSharePicker
*
brief Object to show share menu. *
author Andrew Shapovalov*/
interface QfSharePicker : NSObject /**
brief Create a new object.
*
param view View on what will show share menu. *
param frame Rect to show share window.
*
param datas Datas items to share. *
param block Block of code to select item.
*
author Andrew Shapovalov*/
- (instancetype)initWithView:(NSView*)view frame:(NSRect)frame datasArray:(NSArray*)datas onItemClicked:(QfSharePickerItemClicked)block;
end
#ifdef __cplusplus
}
#endif
- Change .mm file content to this code:
#import "qfsharepicker.h"
#include <QtCore>
/**
brief The private methods and properties of QfSharePicker class. *
author Andrew Shapovalov*/
interface QfSharePicker () <NSSharingServicePickerDelegate, NSSharingServiceDelegate>
/** Sharing service picker.*/
property (nonatomic, retain) NSSharingServicePicker''' picker;
/** Block of code to select item.*/
property (nonatomic, copy) QfSharePickerItemClicked onItemClicked;
end
implementation QfSharePicker
- pragma mark - Init methods
- (instancetype)initWithView:(NSView)view frame:(NSRect)frame datasArray:(NSArray*)datas onItemClicked:(QfSharePickerItemClicked)block {
self = [super init];
if(self) { self.onItemClicked = block; self.picker = [[NSSharingServicePicker alloc] initWithItems: datas]; self.picker.delegate = self; [self.picker showRelativeToRect:frame ofView:view preferredEdge:NSMinXEdge]; }
return self;
}
- pragma mark - NSSharingServicePickerDelegate methods
- (void)sharingServicePicker:(NSSharingServicePicker )sharingServicePicker didChooseSharingService:(NSSharingService)service {
if(self.picker == sharingServicePicker) { if(self.onItemClicked) { self.onItemClicked(service); } }
}
- (id <NSSharingServiceDelegate>)sharingServicePicker:(NSSharingServicePicker )sharingServicePicker delegateForSharingService:(NSSharingService)sharingService {
Q_UNUSED(sharingService);
if(self.picker == sharingServicePicker) { }
return self;
}
- pragma mark - NSSharingServiceDelegate methods
- (void)sharingService:(NSSharingService )sharingService willShareItems:(NSArray)items {
Q_UNUSED(sharingService); Q_UNUSED(items);
//Some code here
}
- (void)sharingService:(NSSharingService )sharingService didFailToShareItems:(NSArray)items error:(NSError )error {
Q_UNUSED(sharingService); Q_UNUSED(items); Q_UNUSED(error);
//Some code here
}
- (void)sharingService:(NSSharingService)sharingService didShareItems:(NSArray )items {
Q_UNUSED(sharingService); Q_UNUSED(items);
//Some code here
}
- pragma mark - Clear memory
- (void)dealloc {
[super dealloc];
[self.picker autorelease]; [self.onItemClicked release]; self.onItemClicked = nil;
}
end
p. More information you can find in official Apple documentation. And you need to see information about class NSSharingServicePickerDelegate.
p. Second class which you need to create is a class to call from C+. This class will call Objective-C/C. To do this you need follow a few steps:
- Create a new C+ class.
- Rename .cpp file to .mm and added it to "OBJECTIVE_SOURCES".
- Change .h file content to code:
#include <QtCore>
#include <QtQuick>
/'''*
class QfShareItem *
brief Manager to use share logic.
*
author Andrew Shapovalov*/
class QfShareItem : public QQuickPaintedItem {
Q_OBJECT Q_PROPERTY(QString shareString READ getShareString WRITE setShareString NOTIFY shareStringChanged) Q_PROPERTY(QUrl shareUrl READ getShareUrl WRITE setShareUrl NOTIFY shareUrlChanged)
private: /** The share string info.*/ QString m_shareString;
/* The share link.*/ QUrl m_shareUrl;
/*
brief Strip all HTML tags from string.
*
param body The source string. *
return Plain text.
*
author Andrew Shapovalov*/
QString stripHTMLTags(QString body);
/**
brief Works with all applications events.
*
param obj The object which call event. *
param event The event of call.
*
author Andrew Shapovalov*/
bool eventFilter(QObject* obj, QEvent* event);
/**
brief Share current content.
*
author Andrew Shapovalov*/
void shareCurrentContent();
public:
/**
brief Create a new object.
*
param parent Parent object. *
author Andrew Shapovalov*/
explicit QfShareItem(QQuickPaintedItem '''parent = 0);
//Others
/'''*
brief Called when current object will redrawing. *
param painter The object for draw data.
*
author Andrew Shapovalov*/
void paint(QPainter painter);
//Getters
/*
brief Get share content string.
*
return Share string content. *
author Andrew Shapovalov*/
inline QString getShareString(){return m_shareString;}
/**
brief Get share url. *
return Share url.
*
author Andrew Shapovalov*/
inline QUrl getShareUrl(){return m_shareUrl;}
//Setters
/**
brief Set share content string.
*
param value Share string content. *
author Andrew Shapovalov*/
inline void setShareString(QString value){m_shareString = value; emit shareStringChanged(m_shareString);}
/**
brief Set share url. *
param value Share url.
*
author Andrew Shapovalov*/
inline void setShareUrl(QUrl value){m_shareUrl = value; emit shareUrlChanged(m_shareUrl);}
/**
brief Share content and url.
*
param text The text of share. *
param url The url to share.
*
author Andrew Shapovalov*/
Q_INVOKABLE void shareContent(QString text = QString(), QUrl url = QUrl());
signals:
/**
brief Called when share content string was changed.
*
param value Share string content. *
author Andrew Shapovalov*/
void shareStringChanged(QString value);
/**
brief Called when share url was changed. *
param value Share url.
*
author Andrew Shapovalov*/
void shareUrlChanged(QUrl value);
/**
brief Called when user select share service.
*
param serviceName The name of selected service. *
author Andrew Shapovalov*/
void selectedService(QString serviceName);
public slots:
};
- Change .mm file content to code:
#include "qfshareitem.h"
#include "qfsharepicker.h"
QString QfShareItem::stripHTMLTags(QString body)
{
body.replace("<br>","");
body.replace("</br>","");
body.replace("</p>","");
body.replace("</td>","");
body.remove(QRegExp("<head>(.''')</head>"));
body.remove(QRegExp("<form(.''')</form>"));
body.remove(QRegExp( "<(.)[</sup>>]'''>"));
return body.trimmed();
}
bool QfShareItem::eventFilter(QObject''' obj, QEvent* event)
{
if(obj == this)
{
if(parentItem())
{
return QObject::eventFilter(parentItem(), event);
}
}
return QObject::eventFilter(obj, event);
}
void QfShareItem::shareCurrentContent()
{
QQuickItem* parentItem = this->parentItem();
if(!m_shareString.isEmpty() && parentItem)
{
QRectF rect = parentItem->mapRectToItem(NULL, parentItem->boundingRect());
NSView* view = reinterpret_cast<NSView *>(parentItem->window()->winId());
NSRect frame = NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height());
m_shareString = m_shareString.replace("<style type=quot;text/cssquot;>a {color:#44a51c;text-decoration:none;}</style>", "");
QString content = stripHTMLTags(m_shareString).trimmed();
NSMutableArray* datas = [NSMutableArray arrayWithObject: content.toNSString()];
if(!m_shareUrl.isEmpty())
{
NSURL* url = [NSURL URLWithString: m_shareUrl.toString().toNSString()];
if(url)
{
[datas addObject: url];
}
}
QfSharePicker* sharePicker = [[QfSharePicker alloc] initWithView:view frame:frame datasArray:datas onItemClicked:nil];
[sharePicker autorelease];
}
}
QfShareItem::QfShareItem(QQuickPaintedItem '''parent) :
QQuickPaintedItem(parent)
{
m_shareString.clear();
m_shareUrl.clear();
connect(this, &QQuickPaintedItem::parentChanged, [this](QQuickItem''' newParent){
if(newParent)
{
newParent->setFiltersChildMouseEvents(true);
}
});
this->installEventFilter(this);
setFlag(QQuickPaintedItem::ItemHasContents, true);
setFlag(QQuickPaintedItem::ItemClipsChildrenToShape, true);
setFlag(QQuickPaintedItem::ItemAcceptsDrops, true);
setRenderTarget(QQuickPaintedItem::InvertedYFramebufferObject);
}
void QfShareItem::paint(QPainter *painter)
{
try
{
Q_UNUSED(painter);
}
catch(std::exception& exception)
{
qDebug()<<"exception: "<<exception.what();
}
}
void QfShareItem::shareContent(QString text, QUrl url)
{
if(!text.isEmpty())
{
m_shareString = text;
}
if(!url.isEmpty())
{
m_shareUrl = url;
}
shareCurrentContent();
}
- Register new type to use in QML.
qmlRegisterType<YourShareItem>(uri, 1, 1, "YourShareItem");
How to use in QML.
Import the new type to your QML file where you need to use share logic and add this code:
Button {
id: btnLink
width: 22
height: 22
onClicked: {
share.shareContent();
}
QfShareItem {
id: share
anchors.fill: btnLink
shareUrl: userPanelView.shareUrl
}
}