diff --git a/src/blackmisc/slot.h b/src/blackmisc/slot.h index 6fd29159f..b7aa0967d 100644 --- a/src/blackmisc/slot.h +++ b/src/blackmisc/slot.h @@ -15,10 +15,45 @@ #include #include #include +#include #include +#include +#include +#include "blackmisc/invoke.h" namespace BlackMisc { + /*! + * Wrapper around QObject::connect which disconnects after the signal has been emitted once. + */ + template + QMetaObject::Connection connectOnce(T *sender, F signal, U *receiver, G &&slot, Qt::ConnectionType type = Qt::AutoConnection) + { + std::promise promise; + auto called = std::make_shared(); + called->clear(); + auto wrapper = [receiver, called, connection = promise.get_future().share(), slot = std::forward(slot)](auto &&... args) + { + if (called->test_and_set()) { return; } + QObject::disconnect(connection.get()); + Private::invokeSlot(slot, receiver, std::forward(args)...); + }; + auto connection = QObject::connect(sender, signal, receiver, std::move(wrapper), type); + promise.set_value(connection); + return connection; + } + + /*! + * Wrapper around QObject::connect which disconnects after the signal has been emitted once. + * \note Slot has no context, so will always be a direct connection, slot will be called in sender's thread. + */ + template + QMetaObject::Connection connectOnce(T *sender, F signal, G &&slot) + { + static_assert(! std::is_member_pointer>::value, "If slot is a pointer to member, a receiver must be supplied"); + return connectOnce(sender, signal, sender, std::forward(slot)); + } + /*! * Callable wrapper for a member function with function signature F. General template, not * implemented; the partial specialization for function signatures is what does the actual work diff --git a/src/blackmisc/valuecache.h b/src/blackmisc/valuecache.h index 032e5eb82..de4eb3feb 100644 --- a/src/blackmisc/valuecache.h +++ b/src/blackmisc/valuecache.h @@ -20,6 +20,7 @@ #include "blackmisc/metaclass.h" #include "blackmisc/propertyindex.h" #include "blackmisc/range.h" +#include "blackmisc/slot.h" #include "blackmisc/statusmessage.h" #include "blackmisc/valuecacheprivate.h" #include "blackmisc/variant.h" @@ -441,12 +442,7 @@ namespace BlackMisc //! \private Connect a function to be called (only once) when the owner's objectName changes. void onOwnerNameChanged(std::function function) { - auto connection = std::make_shared(); - *connection = QObject::connect(m_page->parent(), &QObject::objectNameChanged, [connection, function](const QString &) - { - QObject::disconnect(*connection); - function(); - }); + connectOnce(m_page->parent(), &QObject::objectNameChanged, [function](const QString &) { function(); }); } Private::CValuePage *m_page = (qFatal("Must be initialized"), nullptr); //!< \private