1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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
|