diff options
author | n-peugnet <n.peugnet@free.fr> | 2022-10-06 19:27:24 +0200 |
---|---|---|
committer | n-peugnet <n.peugnet@free.fr> | 2022-10-06 19:27:24 +0200 |
commit | d911b207f49e936b3e992200796110f0749ed150 (patch) | |
tree | 96d20ebb4d074a4c1755e21cb316a52d584daee3 /lib/qt_connection_util.h | |
parent | 8ad8a74152c5701b6ca1f9a00487ba9257a439b4 (diff) | |
parent | 56c2f2e2b809b9077393eb617828f33d144f5634 (diff) | |
download | libquotient-d911b207f49e936b3e992200796110f0749ed150.tar.gz libquotient-d911b207f49e936b3e992200796110f0749ed150.zip |
New upstream version 0.7.0
Diffstat (limited to 'lib/qt_connection_util.h')
-rw-r--r-- | lib/qt_connection_util.h | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/lib/qt_connection_util.h b/lib/qt_connection_util.h new file mode 100644 index 00000000..ef7f6f80 --- /dev/null +++ b/lib/qt_connection_util.h @@ -0,0 +1,129 @@ +// SPDX-FileCopyrightText: 2019 Kitsune Ral <kitsune-ral@users.sf.net> +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include "function_traits.h" + +#include <QtCore/QPointer> + +namespace Quotient { +namespace _impl { + enum ConnectionType { SingleShot, Until }; + + template <ConnectionType CType> + inline auto connect(auto* sender, auto signal, auto* context, auto slotLike, + Qt::ConnectionType connType) + { + auto pConn = std::make_unique<QMetaObject::Connection>(); + auto& c = *pConn; // Save the reference before pConn is moved from + c = QObject::connect( + sender, signal, context, + [slotLike, pConn = std::move(pConn)](const auto&... args) + // The requires-expression below is necessary to prevent Qt + // from eagerly trying to fill the lambda with more arguments + // than slotLike() (i.e., the original slot) can handle + requires requires { slotLike(args...); } { + static_assert(CType == Until || CType == SingleShot, + "Unsupported disconnection type"); + if constexpr (CType == SingleShot) { + // Disconnect early to avoid re-triggers during slotLike() + QObject::disconnect(*pConn); + // Qt kindly keeps slot objects until they do their job, + // even if they disconnect themselves in the process (see + // how doActivate() in qobject.cpp handles c->slotObj). + slotLike(args...); + } else if constexpr (CType == Until) { + if (slotLike(args...)) + QObject::disconnect(*pConn); + } + }, + connType); + return c; + } + + template <typename SlotT, typename ReceiverT> + concept PmfSlot = + (fn_arg_count_v<SlotT> > 0 + && std::is_base_of_v<std::decay_t<fn_arg_t<SlotT, 0>>, ReceiverT>); +} // namespace _impl + +//! \brief Create a connection that self-disconnects when its slot returns true +//! +//! A slot accepted by connectUntil() is different from classic Qt slots +//! in that its return value must be bool, not void. Because of that different +//! signature connectUntil() doesn't accept member functions in the way +//! QObject::connect or Quotient::connectSingleShot do; you should pass a lambda +//! or a pre-bound member function to it. +//! \return whether the connection should be dropped; false means that the +//! connection remains; upon returning true, the slot is disconnected +//! from the signal. +inline auto connectUntil(auto* sender, auto signal, auto* context, + auto smartSlot, + Qt::ConnectionType connType = Qt::AutoConnection) +{ + return _impl::connect<_impl::Until>(sender, signal, context, smartSlot, + connType); +} + +//! Create a connection that self-disconnects after triggering on the signal +template <typename ContextT, typename SlotT> +inline auto connectSingleShot(auto* sender, auto signal, ContextT* context, + SlotT slot, + Qt::ConnectionType connType = Qt::AutoConnection) +{ +#if QT_VERSION_MAJOR >= 6 + return QObject::connect(sender, signal, context, slot, + Qt::ConnectionType(connType + | Qt::SingleShotConnection)); +#else + // In case of classic Qt pointer-to-member-function slots the receiver + // object has to be pre-bound to the slot to make it self-contained + if constexpr (_impl::PmfSlot<SlotT, ContextT>) { + auto&& boundSlot = +# if __cpp_lib_bind_front // Needs Apple Clang 13 (other platforms are fine) + std::bind_front(slot, context); +# else + [context, slot](const auto&... args) + requires requires { (context->*slot)(args...); } + { + (context->*slot)(args...); + }; +# endif + return _impl::connect<_impl::SingleShot>( + sender, signal, context, + std::forward<decltype(boundSlot)>(boundSlot), connType); + } else { + return _impl::connect<_impl::SingleShot>(sender, signal, context, slot, + connType); + } +#endif +} + +/*! \brief A guard pointer that disconnects an interested object upon destruction + * + * It's almost QPointer<> except that you have to initialise it with one + * more additional parameter - a pointer to a QObject that will be + * disconnected from signals of the underlying pointer upon the guard's + * destruction. Note that destructing the guide doesn't destruct either QObject. + */ +template <typename T> +class ConnectionsGuard : public QPointer<T> { +public: + ConnectionsGuard(T* publisher, QObject* subscriber) + : QPointer<T>(publisher), subscriber(subscriber) + {} + ~ConnectionsGuard() + { + if (*this) + (*this)->disconnect(subscriber); + } + ConnectionsGuard(ConnectionsGuard&&) = default; + ConnectionsGuard& operator=(ConnectionsGuard&&) = default; + Q_DISABLE_COPY(ConnectionsGuard) + using QPointer<T>::operator=; + +private: + QObject* subscriber; +}; +} // namespace Quotient |