// SPDX-FileCopyrightText: Copyright (C) 2016 swift Project Community / Contributors // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1 //! \file #ifndef SWIFT_MISC_SLOT_H #define SWIFT_MISC_SLOT_H #include #include #include #include #include #include #include #include "misc/invoke.h" namespace swift::misc { /*! * 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_ns::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_v>, "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 * (similar to how std::function is defined). */ template class CSlot; /*! * Callable wrapper for a member function with function signature R(Args...). * Allows for easy manipulation of slots to be used as callbacks. */ template class CSlot { public: //! Construct an empty slot. CSlot() {} //! Construct a slot from the given member function of the given object. template CSlot(T *object, R (U::*function)(Args...)) : m_object(object), m_function([=](Args... args) { return (object->*function)(args...); }) { Q_ASSERT_X(object, Q_FUNC_INFO, "Need object"); } //! Construct a slot from the given object passing a function and a object template CSlot(T *object, std::function function) : m_object(object), m_function(function) {} //! Call the slot. The behaviour is undefined if the slot is empty. R operator()(Args... args) const { Q_ASSERT_X(m_function, Q_FUNC_INFO, "Empty CSlot was called"); return m_function(args...); } //! Call function "de-coupled" in original thread bool singleShot(Args... args) const { // does NOT return the values of m_function! if (!m_object || !m_function) { return false; } QTimer::singleShot(0, m_object.data(), std::bind(*this, std::forward(args)...)); return true; } //! Returns the object which the slot belongs to. //! Use this as the third argument to QObject::connect to ensure the slot is called in the correct thread. QObject *object() const { return m_object.data(); } //! Set the object which the slot belongs to. //! Use this as the third argument to QObject::connect to ensure the slot is called in the correct thread. void setObject(QObject *object) { Q_ASSERT_X(hasNullObject(), Q_FUNC_INFO, "Can only set, not change the object"); m_object = object; } //! True if the slot can be called, false if it is empty. operator bool() const { return !this->isEmpty() && !this->hasNullObject(); } //! True if the slot is empty or object is null, false if it can be called. bool operator!() const { return this->isEmpty() || this->hasNullObject(); } //! True if the slot is empty, false if it can be called. bool isEmpty() const { return !m_function; } //! True if the object is null bool hasNullObject() const { return m_object.isNull(); } private: QPointer m_object; std::function m_function; }; } // namespace swift::misc #endif