// Copyright (C) 2021 The Qt Company Ltd. // Copyright (C) 2019 Luxoft Sweden AB // Copyright (C) 2018 Pelagicore AG // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QIFPENDINGREPLY_H #define QIFPENDINGREPLY_H #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QIfPendingReplyWatcherPrivate; Q_QTINTERFACEFRAMEWORK_EXPORT void qifRegisterPendingReplyBasicTypes(); class Q_QTINTERFACEFRAMEWORK_EXPORT QIfPendingReplyWatcher : public QObject { Q_OBJECT Q_PROPERTY(QVariant value READ value NOTIFY valueChanged) Q_PROPERTY(bool valid READ isValid CONSTANT) Q_PROPERTY(bool resultAvailable READ isResultAvailable NOTIFY valueChanged) Q_PROPERTY(bool success READ isSuccessful NOTIFY valueChanged) public: QVariant value() const; bool isValid() const; bool isResultAvailable() const; bool isSuccessful() const; Q_INVOKABLE void setSuccess(const QVariant &value); Q_INVOKABLE void setFailed(); Q_INVOKABLE void then(const QJSValue &success, const QJSValue &failed = QJSValue()); Q_SIGNALS: void replyFailed(); void replySuccess(); void valueChanged(const QVariant &value); private: explicit QIfPendingReplyWatcher(int userType); Q_DECLARE_PRIVATE(QIfPendingReplyWatcher) friend class QIfPendingReplyBase; }; class Q_QTINTERFACEFRAMEWORK_EXPORT QIfPendingReplyBase { Q_GADGET Q_PROPERTY(QIfPendingReplyWatcher* watcher READ watcher) Q_PROPERTY(QVariant value READ value) Q_PROPERTY(bool valid READ isValid) Q_PROPERTY(bool resultAvailable READ isResultAvailable) Q_PROPERTY(bool success READ isSuccessful) public: explicit QIfPendingReplyBase(int userType); QIfPendingReplyBase() = default; QIfPendingReplyBase(const QIfPendingReplyBase & other); ~QIfPendingReplyBase() = default; QIfPendingReplyBase& operator=(const QIfPendingReplyBase&) = default; QIfPendingReplyBase& operator=(QIfPendingReplyBase&&) = default; QIfPendingReplyWatcher* watcher() const; QVariant value() const; bool isValid() const; bool isResultAvailable() const; bool isSuccessful() const; Q_INVOKABLE void then(const QJSValue &success, const QJSValue &failed = QJSValue()); Q_INVOKABLE void setSuccess(const QVariant & value); Q_INVOKABLE void setFailed(); protected: void setSuccessNoCheck(const QVariant & value); QSharedPointer m_watcher; }; template class QIfPendingReply : public QIfPendingReplyBase { public: QIfPendingReply(const T &successValue) : QIfPendingReply() { setSuccess(successValue); } QIfPendingReply() : QIfPendingReplyBase(qMetaTypeId()) {} using QIfPendingReplyBase::setSuccess; void setSuccess(const T &val) { setSuccessNoCheck(QVariant::fromValue(val)); } T reply() const { return m_watcher->value().template value(); } using QIfPendingReplyBase::then; void then(const std::function &success, const std::function &failed = std::function()) { if (isResultAvailable()) { if (isSuccessful() && success) success(reply()); else if (failed) failed(); } else { QWeakPointer w = m_watcher; if (success) { QObject::connect(watcher(), &QIfPendingReplyWatcher::replySuccess, watcher(), [success, w]() { if (w) success(w.toStrongRef()->value().value()); }); } if (failed) QObject::connect(watcher(), &QIfPendingReplyWatcher::replyFailed, watcher(), failed); } } static QIfPendingReply createFailedReply() { QIfPendingReply reply; reply.setFailed(); return reply; } }; template <> class QIfPendingReply : public QIfPendingReplyBase { public: QIfPendingReply(const QVariant &successValue) : QIfPendingReply() { setSuccess(successValue); } QIfPendingReply() : QIfPendingReplyBase(qMetaTypeId()) {} void setSuccess(const QVariant &val) { setSuccessNoCheck(val); } QVariant reply() const { return m_watcher->value(); } void then(const std::function &success, const std::function &failed = std::function()) { if (isResultAvailable()) { if (isSuccessful() && success) success(reply()); else if (failed) failed(); } else { QWeakPointer w = m_watcher; if (success) { QObject::connect(watcher(), &QIfPendingReplyWatcher::replySuccess, watcher(), [success, w]() { if (w) success(w.toStrongRef()->value()); }); } if (failed) QObject::connect(watcher(), &QIfPendingReplyWatcher::replyFailed, watcher(), failed); } } static QIfPendingReply createFailedReply() { QIfPendingReply reply; reply.setFailed(); return reply; } }; template <> class QIfPendingReply : public QIfPendingReplyBase { public: QIfPendingReply() : QIfPendingReplyBase(qMetaTypeId()) {} using QIfPendingReplyBase::setSuccess; void setSuccess() { setSuccessNoCheck(QVariant()); } void reply() const { return; } void then(const std::function &success, const std::function &failed = std::function()) { if (isResultAvailable()) { if (isSuccessful() && success) success(); else if (failed) failed(); } else { if (success) QObject::connect(watcher(), &QIfPendingReplyWatcher::replySuccess, watcher(), success); if (failed) QObject::connect(watcher(), &QIfPendingReplyWatcher::replyFailed, watcher(), failed); } } static QIfPendingReply createFailedReply() { QIfPendingReply reply; reply.setFailed(); return reply; } }; //Workaround for QTBUG-83664 //If T is a enum template Q_INLINE_TEMPLATE typename std::enable_if::Value, void>::type qIfRegisterPendingReplyType(const char *name = nullptr) { qRegisterMetaType(); QString n; if (name) { n = QLatin1String(name); } else { QMetaEnum me = QMetaEnum::fromType(); if (me.isValid() && me.isFlag()) n = QLatin1String(me.scope()) + QStringLiteral("::") + QLatin1String(me.name()); else n = QLatin1String(QMetaType(qMetaTypeId()).name()); } const QString t_name = QStringLiteral("QIfPendingReply<") + n + QStringLiteral(">"); qRegisterMetaType(qPrintable(t_name)); } //If T is NOT a enum template Q_INLINE_TEMPLATE typename std::enable_if::Value, void>::type qIfRegisterPendingReplyType(const char *name = nullptr) { qRegisterMetaType(); const char* n = name ? name : QMetaType(qMetaTypeId()).name(); const QString t_name = QStringLiteral("QIfPendingReply<") + QLatin1String(n) + QStringLiteral(">"); qRegisterMetaType(qPrintable(t_name)); } QT_END_NAMESPACE #endif // QIFPENDINGREPLY_H