10#include "private/imageset_p.h"
11#include "private/svg_p.h"
17#include <QCoreApplication>
20#include <QRegularExpression>
21#include <QStringBuilder>
22#include <QXmlStreamReader>
23#include <QXmlStreamWriter>
25#include <KCompressionDevice>
26#include <KConfigGroup>
32size_t qHash(
const KSvg::SvgPrivate::CacheId &
id,
size_t seed)
34 std::array<size_t, 10> parts = {
37 ::qHash(
id.elementName),
40 ::qHash(
id.scaleFactor),
42 ::qHash(
id.styleSheet),
43 ::qHash(
id.extraFlags),
44 ::qHash(
id.lastModified),
46 return qHashRange(parts.begin(), parts.end(), seed);
51 std::vector<size_t> parts;
52 for (
const QColor &c : std::as_const(colors)) {
53 parts.push_back(::qHash(c.red()));
54 parts.push_back(::qHash(c.green()));
55 parts.push_back(::qHash(c.blue()));
56 parts.push_back(::qHash(c.alpha()));
58 return qHashRange(parts.begin(), parts.end(), seed);
63class SvgRectsCacheSingleton
69Q_GLOBAL_STATIC(SvgRectsCacheSingleton, privateSvgRectsCacheSelf)
71const size_t SvgRectsCache::s_seed = 0x9e3779b9;
73SharedSvgRenderer::SharedSvgRenderer(
QObject *parent)
85 m_filename = filename;
86 m_styleSheet = styleSheet;
87 m_interestingElements = interestingElements;
88 load(file.readAll(), styleSheet, interestingElements);
94 load(contents, styleSheet, interestingElements);
97void SharedSvgRenderer::reload()
104 load(file.readAll(), m_styleSheet, m_interestingElements);
110 if (!styleSheet.
isEmpty() && contents.
contains(
"current-color-scheme")) {
115 QBuffer buffer(&processedContents);
118 while (!reader.atEnd()) {
122 writer.writeAttributes(reader.attributes());
123 writer.writeCharacters(styleSheet);
124 writer.writeEndElement();
129 writer.writeCurrentToken(reader);
143 Q_ASSERT(idExpr.isValid());
145 auto matchIt = idExpr.globalMatch(contentsAsString);
146 while (matchIt.hasNext()) {
147 auto match = matchIt.next();
150 QRectF elementRect = boundsOnElement(elementId);
152 interestingElements.
insert(elementId, elementRect);
159SvgRectsCache::SvgRectsCache(
QObject *parent)
165 m_configSyncTimer =
new QTimer(
this);
166 m_configSyncTimer->setSingleShot(
true);
167 m_configSyncTimer->setInterval(5000);
169 m_svgElementsCache->sync();
173SvgRectsCache *SvgRectsCache::instance()
175 return &privateSvgRectsCacheSelf()->self;
178void SvgRectsCache::insert(KSvg::SvgPrivate::CacheId cacheId,
const QRectF &rect,
unsigned int lastModified)
180 insert(
qHash(cacheId, SvgRectsCache::s_seed), cacheId.filePath, rect, lastModified);
183void SvgRectsCache::insert(
size_t id,
const QString &filePath,
const QRectF &rect,
unsigned int lastModified)
185 const unsigned int savedTime = lastModifiedTimeFromCache(filePath);
187 if (savedTime == lastModified && m_localRectCache.contains(
id)) {
191 m_localRectCache.insert(
id, rect);
198 m_invalidElements[filePath] << id;
199 imageGroup.writeEntry(
"Invalidelements", m_invalidElements[filePath].values());
204 if (savedTime != lastModified) {
205 m_lastModifiedTimes[filePath] = lastModified;
206 imageGroup.writeEntry(
"LastModified", lastModified);
207 Q_EMIT lastModifiedChanged(filePath, lastModified);
211bool SvgRectsCache::findElementRect(KSvg::SvgPrivate::CacheId cacheId,
QRectF &rect)
213 return findElementRect(
qHash(cacheId, SvgRectsCache::s_seed), cacheId.filePath, rect);
216bool SvgRectsCache::findElementRect(
size_t id,
QStringView filePath,
QRectF &rect)
218 auto it = m_localRectCache.find(
id);
220 if (it == m_localRectCache.end()) {
221 auto elements = m_invalidElements.value(filePath.
toString());
222 if (elements.contains(
id)) {
234bool SvgRectsCache::loadImageFromCache(
const QString &path, uint lastModified)
242 unsigned int savedTime = lastModifiedTimeFromCache(path);
245 if (lastModified != savedTime) {
246 imageGroup.deleteGroup();
251 auto &elements = m_invalidElements[
path];
252 if (elements.isEmpty()) {
256 for (
const auto &key : imageGroup.keyList()) {
258 uint keyUInt = key.toUInt(&ok);
260 const QRectF rect = imageGroup.readEntry(key,
QRectF());
261 m_localRectCache.insert(keyUInt, rect);
268void SvgRectsCache::dropImageFromCache(
const QString &path)
271 imageGroup.deleteGroup();
279 auto it = m_sizeHintsForId.constFind(pathId);
280 if (it == m_sizeHintsForId.constEnd()) {
284 for (
const auto &token : encoded) {
286 if (parts.size() != 2) {
289 QSize size =
QSize(parts[0].toDouble(), parts[1].toDouble());
294 m_sizeHintsForId[pathId] = sizes;
301void SvgRectsCache::insertSizeHintForId(
const QString &path,
const QString &
id,
const QSizeF &size)
306 for (
const auto &s : list) {
313 imageGroup.writeEntry(
id, sizeListToString(m_sizeHintsForId[path %
id]));
317QString SvgRectsCache::iconThemePath()
319 if (!m_iconThemePath.isEmpty()) {
320 return m_iconThemePath;
323 KConfigGroup imageGroup(m_svgElementsCache, QStringLiteral(
"General"));
324 m_iconThemePath = imageGroup.readEntry(QStringLiteral(
"IconThemePath"),
QString());
326 return m_iconThemePath;
329void SvgRectsCache::setIconThemePath(
const QString &path)
331 m_iconThemePath =
path;
332 KConfigGroup imageGroup(m_svgElementsCache, QStringLiteral(
"General"));
333 imageGroup.writeEntry(QStringLiteral(
"IconThemePath"), path);
337void SvgRectsCache::setNaturalSize(
const QString &path,
const QSizeF &size)
342 imageGroup.writeEntry(QStringLiteral(
"NaturalSize"), size);
351 return imageGroup.readEntry(QStringLiteral(
"NaturalSize"),
QSizeF());
368unsigned int SvgRectsCache::lastModifiedTimeFromCache(
const QString &filePath)
370 const auto &i = m_lastModifiedTimes.constFind(filePath);
371 if (i != m_lastModifiedTimes.constEnd()) {
376 const unsigned int savedTime = imageGroup.readEntry(
"LastModified", 0);
377 m_lastModifiedTimes[filePath] = savedTime;
381void SvgRectsCache::updateLastModified(
const QString &filePath,
unsigned int lastModified)
384 const unsigned int savedTime = lastModifiedTimeFromCache(filePath);
386 if (savedTime != lastModified) {
387 m_lastModifiedTimes[filePath] = lastModified;
388 imageGroup.writeEntry(
"LastModified", lastModified);
390 Q_EMIT lastModifiedChanged(filePath, lastModified);
394SvgPrivate::SvgPrivate(Svg *svg)
399 , devicePixelRatio(1.0)
401 , multipleImages(false)
403 , fromCurrentImageSet(false)
404 , cacheRendering(true)
409SvgPrivate::~SvgPrivate()
414size_t SvgPrivate::paletteId(
const QPalette &palette,
const QColor &positive,
const QColor &neutral,
const QColor &negative)
const
416 std::array<size_t, 4> parts = {
418 ::qHash(positive.
rgba()),
419 ::qHash(neutral.
rgba()),
420 ::qHash(negative.
rgba()),
422 return qHashRange(parts.begin(), parts.end(), SvgRectsCache::s_seed);
426SvgPrivate::CacheId SvgPrivate::cacheId(
QStringView elementId)
const
428 auto idSize = size.
isValid() && size != naturalSize ? size :
QSizeF{-1.0, -1.0};
429 return CacheId{idSize.width(), idSize.height(),
path, elementId.
toString(),
status, devicePixelRatio, -1, 0, 0, lastModified};
435 std::vector<size_t> parts;
436 const auto colors = colorOverrides.values();
437 for (
const QColor &c : std::as_const(colors)) {
438 parts.push_back(::qHash(c.red()));
439 parts.push_back(::qHash(c.green()));
440 parts.push_back(::qHash(c.blue()));
441 parts.push_back(::qHash(c.alpha()));
443 const size_t colorsHash = qHashRange(parts.begin(), parts.end(), SvgRectsCache::s_seed);
445 auto cacheId = CacheId{double(size.
width()), double(size.
height()),
path, id,
status, devicePixelRatio, colorSet, colorsHash, 0, lastModified};
449bool SvgPrivate::setImagePath(
const QString &imagePath)
451 QString actualPath = imagePath;
456 isAbsoluteFile =
true;
463 isAbsoluteFile =
true;
466 bool isThemed = !actualPath.
isEmpty() && !isAbsoluteFile;
469 if (isThemed == themed && ((themed && themePath == actualPath) || (!themed && path == actualPath))) {
477 bool updateNeeded =
true;
485 bool oldfromCurrentImageSet = fromCurrentImageSet;
486 fromCurrentImageSet = isThemed && actualImageSet()->currentImageSetHasImage(imagePath);
488 if (fromCurrentImageSet != oldfromCurrentImageSet) {
489 Q_EMIT q->fromCurrentImageSetChanged(fromCurrentImageSet);
493 themePath = actualPath;
494 path = actualImageSet()->imagePath(themePath);
496 imageSetChangedConnection =
QObject::connect(actualImageSet(), &ImageSet::imageSetChanged, q, [
this]() {
500 imageSetChangedConnection =
QObject::connect(actualImageSet(), &ImageSet::imageSetChanged, q, [
this]() {
513 lastModifiedDate = info.lastModified();
517 const bool imageWasCached = SvgRectsCache::instance()->loadImageFromCache(path, lastModified);
519 if (!imageWasCached) {
520 auto i = s_renderers.constBegin();
521 while (i != s_renderers.constEnd()) {
522 if (i.key().contains(path)) {
533 naturalSize = SvgRectsCache::instance()->naturalSize(path);
534 if (naturalSize.isEmpty()) {
536 naturalSize = renderer->defaultSize();
537 SvgRectsCache::instance()->setNaturalSize(path, naturalSize);
542 Q_EMIT q->imagePathChanged();
547ImageSet *SvgPrivate::actualImageSet()
564 const QList<QSizeF> elementSizeHints = SvgRectsCache::instance()->sizeHintsForId(path, elementId);
566 if (!elementSizeHints.
isEmpty()) {
569 for (
const auto &hint : elementSizeHints) {
571 && (!bestFit.isValid() || (bestFit.width() * bestFit.height()) > (
hint.width() *
hint.height()))) {
576 if (bestFit.isValid()) {
582 if (elementId.
isEmpty() || !q->hasElement(actualElementId)) {
583 actualElementId = elementId;
587 size = s.
toSize() * ratio;
589 size = elementRect(actualElementId).
size().
toSize() * ratio;
596 const QString id = cachePath(actualElementId, size);
599 if (cacheRendering && lastModified == SvgRectsCache::instance()->lastModifiedTimeFromCache(path) && actualImageSet()->d->findInCache(
id, p, lastModified)) {
607 QRectF finalRect = makeUniform(renderer->boundsOnElement(actualElementId),
QRect(
QPoint(0, 0), size));
616 if (actualElementId.
isEmpty()) {
617 renderer->render(&renderPainter, finalRect);
619 renderer->render(&renderPainter, actualElementId, finalRect);
625 if (cacheRendering) {
629 SvgRectsCache::instance()->updateLastModified(path, lastModified);
634void SvgPrivate::createRenderer()
642 path = actualImageSet()->imagePath(themePath);
645 qCWarning(LOG_KSVG) <<
"No image path found for" << themePath;
651 if (!colorOverrides.isEmpty()) {
652 if (stylesheetOverride.isEmpty()) {
653 stylesheetOverride = actualImageSet()->d->svgStyleSheet(q);
655 styleSheet = stylesheetOverride;
657 styleSheet = actualImageSet()->d->svgStyleSheet(q);
664 if (it != s_renderers.constEnd()) {
665 renderer = it.value();
668 renderer =
new SharedSvgRenderer();
671 renderer =
new SharedSvgRenderer(path, styleSheet, interestingElements);
678 while (i.hasNext()) {
680 const QString &elementId = i.key();
682 const QRectF &elementRect = i.value();
684 originalId.
replace(sizeHintedKeyExpr, QStringLiteral(
"\\3"));
685 SvgRectsCache::instance()->insertSizeHintForId(path, originalId, elementRect.
size().
toSize());
687 const CacheId cacheId{.width = -1.0,
690 .elementName = elementId,
692 .scaleFactor = devicePixelRatio,
696 .lastModified = lastModified};
697 SvgRectsCache::instance()->
insert(cacheId, elementRect, lastModified);
701 s_renderers[styleCrc +
path] = renderer;
705 size = renderer->defaultSize();
709void SvgPrivate::eraseRenderer()
711 if (renderer && renderer->ref.loadRelaxed() == 2) {
713 s_renderers.erase(s_renderers.find(styleCrc + path));
727 path = actualImageSet()->imagePath(themePath);
740 const CacheId cacheId = SvgPrivate::cacheId(elementId);
741 bool found = SvgRectsCache::instance()->findElementRect(cacheId, rect);
744 rect = findAndCacheElementRect(elementId);
753 const CacheId cacheId = SvgPrivate::cacheId(elementId);
757 auto elementIdString = elementId.
toString();
760 QRectF elementRect = renderer->elementExists(elementIdString)
761 ? renderer->transformForElement(elementIdString).map(renderer->boundsOnElement(elementIdString)).boundingRect()
764 naturalSize = renderer->defaultSize();
766 qreal dx = size.
width() / renderer->defaultSize().width();
767 qreal dy = size.
height() / renderer->defaultSize().height();
769 elementRect =
QRectF(elementRect.
x() * dx, elementRect.
y() * dy, elementRect.
width() * dx, elementRect.
height() * dy);
770 SvgRectsCache::instance()->insert(cacheId, elementRect, lastModified);
782qreal SvgPrivate::closestDistance(qreal to, qreal from)
785 if (qFuzzyCompare(to, from)) {
787 }
else if (to > from) {
788 qreal b = to - from - 1;
789 return (qAbs(a) > qAbs(b)) ? b : a;
791 qreal b = 1 + to - from;
792 return (qAbs(a) > qAbs(b)) ? b : a;
798 if (qFuzzyIsNull(orig.
x()) || qFuzzyIsNull(orig.
y())) {
806 qreal div_x = dst.
x() / orig.
x();
807 qreal div_y = dst.
y() / orig.
y();
810 if (!qFuzzyIsNull(div_x) && !qFuzzyCompare(div_w, div_x)) {
811 qreal rem_orig = orig.
x() - (floor(orig.
x()));
812 qreal rem_dst = dst.
x() - (floor(dst.
x()));
813 qreal offset = closestDistance(rem_dst, rem_orig);
814 res.translate(offset + offset * div_w, 0);
815 res.setWidth(res.width() + offset);
818 if (!qFuzzyIsNull(div_y) && !qFuzzyCompare(div_h, div_y)) {
819 qreal rem_orig = orig.
y() - (floor(orig.
y()));
820 qreal rem_dst = dst.
y() - (floor(dst.
y()));
821 qreal offset = closestDistance(rem_dst, rem_orig);
822 res.translate(0, offset + offset * div_h);
823 res.setHeight(res.height() + offset);
829void SvgPrivate::imageSetChanged()
831 if (q->imagePath().isEmpty()) {
838 setImagePath(currentPath);
842 Q_EMIT q->repaintNeeded();
843 Q_EMIT q->imageSetChanged(q->imageSet());
846void SvgPrivate::colorsChanged()
849 qCDebug(LOG_KSVG) <<
"repaint needed from colorsChanged";
851 Q_EMIT q->repaintNeeded();
859 , d(new SvgPrivate(this))
861 connect(SvgRectsCache::instance(), &SvgRectsCache::lastModifiedChanged,
this, [
this](
const QString &filePath,
unsigned int lastModified) {
862 if (d->lastModified != lastModified && filePath == d->path) {
863 d->lastModified = lastModified;
880 d->devicePixelRatio = ratio;
887 return d->devicePixelRatio;
892 if (elementID.
isNull() || d->multipleImages) {
893 return d->findInCache(elementID, d->devicePixelRatio, size());
895 return d->findInCache(elementID, d->devicePixelRatio);
901 QPixmap pix(d->findInCache(elementID, d->devicePixelRatio, size));
907 Q_ASSERT(painter->
device());
909 QPixmap pix((elementID.
isNull() || d->multipleImages) ? d->findInCache(elementID, ratio, size()) : d->findInCache(elementID, ratio));
925 Q_ASSERT(painter->
device());
927 QPixmap pix(d->findInCache(elementID, ratio, rect.
size()));
934 Q_ASSERT(painter->
device());
936 QPixmap pix(d->findInCache(elementID, ratio,
QSizeF(width, height)));
942 if (d->size.isEmpty()) {
943 d->size = d->naturalSize;
946 return {std::round(d->size.width()), std::round(d->size.height())};
956 if (qFuzzyCompare(size.
width(), d->size.width()) && qFuzzyCompare(size.
height(), d->size.height())) {
966 if (qFuzzyCompare(d->naturalSize.width(), d->size.width()) && qFuzzyCompare(d->naturalSize.height(), d->size.height())) {
970 d->size = d->naturalSize;
976 const QSizeF s = d->elementRect(elementId).size();
977 return {std::round(s.
width()), std::round(s.
height())};
982 const QSizeF s = d->elementRect(elementId).size();
983 return {std::round(s.
width()), std::round(s.
height())};
988 return d->elementRect(elementId);
993 return d->elementRect(elementId);
1003 if (elementId.
isEmpty() || (d->path.isNull() && d->themePath.isNull())) {
1007 return d->elementRect(elementId).isValid();
1012 if (d->path.isNull() && d->themePath.isNull()) {
1017 QSizeF naturalSize = SvgRectsCache::instance()->naturalSize(d->path);
1025 d->createRenderer();
1026 return d->renderer->isValid();
1031 d->multipleImages = multiple;
1036 return d->multipleImages;
1041 if (d->setImagePath(svgFilePath)) {
1048 return d->themed ? d->themePath : d->path;
1053 d->cacheRendering = useCache;
1059 return d->cacheRendering;
1062bool Svg::fromCurrentImageSet()
const
1064 return d->fromCurrentImageSet;
1069 if (!theme || theme == d->theme.data()) {
1074 disconnect(d->theme.data(),
nullptr,
this,
nullptr);
1079 d->imageSetChanged();
1084 return d->actualImageSet();
1089 if (
status == d->status) {
1099Svg::Status Svg::status()
const
1107 if (convertedSet == d->colorSet) {
1111 d->colorSet = convertedSet;
1117Svg::ColorSet Svg::colorSet()
const
1119 return Svg::ColorSet(d->colorSet);
1122QColor Svg::color(StyleSheetColor colorName)
const
1124 auto it = d->colorOverrides.constFind(colorName);
1125 if (it != d->colorOverrides.constEnd()) {
1128 return d->actualImageSet()->d->namedColor(colorName,
this);
1131void Svg::setColor(StyleSheetColor colorName,
const QColor &color)
1133 if (d->colorOverrides.value(colorName) == color) {
1138 d->colorOverrides[colorName] = color;
1140 d->colorOverrides.remove(colorName);
1142 d->stylesheetOverride.clear();
1148void Svg::clearColorOverrides()
1150 d->colorOverrides.clear();
1151 d->stylesheetOverride.clear();
1158#include "moc_svg.cpp"
1159#include "private/moc_svg_p.cpp"
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
Provides an SVG with borders.
Interface to the Svg image set.
virtual void setImagePath(const QString &svgFilePath)
This method sets the SVG file to render.
Q_INVOKABLE QImage image(const QSize &size, const QString &elementID=QString())
This method returns an image of the SVG represented by this object.
void statusChanged(KSvg::Svg::Status status)
This signal is emitted when the status has changed.
void setStatus(Svg::Status status)
This method sets the image in a selected status.
void setContainsMultipleImages(bool multiple)
This method sets whether the SVG contains a single image or multiple ones.
Q_INVOKABLE void resize()
This method resizes the rendered image to the natural size of the SVG.
void setColorSet(ColorSet colorSet)
This method sets a color set for the SVG.
Q_INVOKABLE QPixmap pixmap(const QString &elementID=QString())
This method returns a pixmap of the SVG represented by this object.
qreal devicePixelRatio() const
This method returns the device pixel ratio for this Svg.
bool containsMultipleImages() const
This method returns whether the SVG contains multiple images.
void setDevicePixelRatio(qreal factor)
This method sets the device pixel ratio for the Svg.
void colorSetChanged(KSvg::Svg::ColorSet colorSet)
This signal is emitted when the color set has changed.
void sizeChanged()
This signal is emitted whenever the size has changed.
Q_INVOKABLE QRectF elementRect(const QString &elementId) const
This method returns the bounding rect of a given element.
Q_INVOKABLE void paint(QPainter *painter, const QPointF &point, const QString &elementID=QString())
This method paints all or part of the SVG represented by this object.
ImageSet * imageSet() const
This method returns the KSvg::ImageSet used by this Svg object.
void repaintNeeded()
This signal is emitted whenever the SVG data has changed in such a way that a repaint is required.
void imageSetChanged(ImageSet *imageSet)
This signal is emitted when the image set has changed.
bool isUsingRenderingCache() const
Whether the rendering cache is being used.
Q_INVOKABLE bool hasElement(const QString &elementId) const
This method checks whether an element exists in the loaded SVG.
Q_INVOKABLE bool isValid() const
This method checks whether this object is backed by a valid SVG file.
Q_INVOKABLE QSizeF elementSize(const QString &elementId) const
This method returns the size of a given element.
void setUsingRenderingCache(bool useCache)
This method sets whether or not to cache the results of rendering to pixmaps.
void setImageSet(KSvg::ImageSet *theme)
This method sets the KSvg::ImageSet to use with this Svg object.
Q_SCRIPTABLE CaptureState status()
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
QAction * hint(const QObject *recvr, const char *slot, QObject *parent)
QAction * load(const QObject *recvr, const char *slot, QObject *parent)
QString path(const QString &relativePath)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
KTEXTEDITOR_EXPORT size_t qHash(KTextEditor::Cursor cursor, size_t seed=0) noexcept
const char * constData() const const
bool contains(QByteArrayView bv) const const
void reserve(qsizetype size)
qsizetype size() const const
bool isValid() const const
bool isValid() const const
qint64 toSecsSinceEpoch() const const
bool isAbsolutePath(const QString &path)
bool exists() const const
iterator insert(const Key &key, const T &value)
bool isEmpty() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
virtual bool eventFilter(QObject *watched, QEvent *event)
T qobject_cast(QObject *object)
qreal devicePixelRatio() const const
QPaintDevice * device() const const
void drawPixmap(const QPoint &point, const QPixmap &pixmap)
qint64 cacheKey() const const
void fill(const QColor &color)
bool isNull() const const
void setDevicePixelRatio(qreal scaleFactor)
QImage toImage() const const
qreal height() const const
bool isValid() const const
QSizeF size() const const
qreal width() const const
bool isEmpty() const const
qreal height() const const
bool isEmpty() const const
bool isValid() const const
QSize toSize() const const
qreal width() const const
QString writableLocation(StandardLocation type)
QString & append(QChar ch)
QString fromLatin1(QByteArrayView str)
QString & insert(qsizetype position, QChar ch)
bool isEmpty() const const
bool isNull() const const
QString number(double n, char format, int precision)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
qsizetype size() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QByteArray toUtf8() const const
bool isEmpty() const const
QString toString() const const
bool load(QXmlStreamReader *contents)
QFuture< typename qValueType< Iterator >::value_type > filtered(Iterator begin, Iterator end, KeepFunctor &&filterFunction)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)