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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
use crate::{q_meta_object::Connection, ConnectionType, QBox, QObject, QPtr};
use cpp_core::{CastInto, CppBox, CppDeletable, Ptr, Ref, StaticUpcast};
use std::ffi::CStr;
use std::fmt;
use std::marker::PhantomData;

/// Argument types compatible for signal connection.
///
/// Qt allows to connect senders to receivers if their argument types are the same.
/// Additionally, Qt allows receivers to have fewer arguments than the sender.
/// Other arguments are simply omitted in such a connection.
///
/// Note that Qt also allows to connect senders to receivers when their argument types
/// are not the same but there is a conversion from sender's argument types
/// to receiver's corresponding argument types. This ability is not exposed in Rust
/// wrapper's API.
///
/// Argument types are expressed as a tuple.
/// `ArgumentsCompatible<T1>` is implemented for `T2` tuple if
/// `T1` tuple can be constructed by removing some elements from the end of `T2`.
///
/// For instance, `ArgumentsCompatible<T>` and `ArgumentsCompatible<()>` are implemented
/// for every `T`.
///
/// `ArgumentsCompatible` is implemented for tuples with up to 16 items.
pub trait ArgumentsCompatible<T> {}

/// Reference to a particular signal or slot of a particular object.
///
/// A `Receiver` can be used as the receiving side of a Qt signal connection.
/// The `Arguments` generic argument specifies argument types of this signal or slot.
pub struct Receiver<Arguments> {
    q_object: Ref<QObject>,
    receiver_id: &'static CStr,
    _marker: PhantomData<Arguments>,
}

impl<A> Clone for Receiver<A> {
    fn clone(&self) -> Self {
        Receiver {
            q_object: self.q_object,
            receiver_id: self.receiver_id,
            _marker: PhantomData,
        }
    }
}
impl<A> Copy for Receiver<A> {}

impl<A> fmt::Debug for Receiver<A> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Receiver")
            .field("q_object", &self.q_object)
            .field("receiver_id", &self.receiver_id)
            .finish()
    }
}

impl<A> Receiver<A> {
    /// Creates a `Receiver` than references a signal or a slot of `q_object` identified by
    /// `receiver_id`.
    ///
    /// This function should not be used manually. It's normally called from functions
    /// generated by `ritual`. `receiver_id` is the ID returned by Qt's `SIGNAL` and `SLOT`
    /// C++ macros.
    ///
    /// # Safety
    ///
    /// `q_object` must contain a valid pointer to a `QObject`-based object. The object
    /// must outlive the created `Receiver` object.
    pub unsafe fn new(q_object: impl CastInto<Ref<QObject>>, receiver_id: &'static CStr) -> Self {
        Self {
            q_object: q_object.cast_into(),
            receiver_id,
            _marker: PhantomData,
        }
    }
}

/// Reference to a particular signal of a particular object.
///
/// The `Arguments` generic argument specifies argument types of this signal.
pub struct Signal<Arguments>(Receiver<Arguments>);

impl<A> Clone for Signal<A> {
    fn clone(&self) -> Self {
        Signal(self.0)
    }
}

impl<A> Copy for Signal<A> {}

impl<A> fmt::Debug for Signal<A> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Signal")
            .field("qobject", &self.0.q_object)
            .field("receiver_id", &self.0.receiver_id)
            .finish()
    }
}

impl<A> Signal<A> {
    /// Creates a `Signal` than references a signal of `q_object` identified by
    /// `receiver_id`.
    ///
    /// This function should not be used manually. It's normally called from functions
    /// generated by `ritual`. `receiver_id` is the ID returned by Qt's `SIGNAL`
    /// C++ macro.
    ///
    /// # Safety
    ///
    /// `q_object` must contain a valid pointer to a `QObject`-based object. The object
    /// must outlive the created `Signal` object.
    pub unsafe fn new(q_object: impl CastInto<Ref<QObject>>, receiver_id: &'static CStr) -> Self {
        Signal(Receiver::new(q_object, receiver_id))
    }
}

/// Values that can be used for specifying the receiving side of a Qt signal connection.
pub trait AsReceiver {
    /// Argument types expected by this receiver.
    type Arguments;
    /// Returns information about this receiver.
    fn as_receiver(&self) -> Receiver<Self::Arguments>;
}

impl<A> AsReceiver for Receiver<A> {
    type Arguments = A;
    fn as_receiver(&self) -> Receiver<A> {
        *self
    }
}

impl<A> AsReceiver for Signal<A> {
    type Arguments = A;
    fn as_receiver(&self) -> Receiver<A> {
        self.0
    }
}

impl<T> AsReceiver for Ptr<T>
where
    T: AsReceiver,
{
    type Arguments = <T as AsReceiver>::Arguments;
    fn as_receiver(&self) -> Receiver<Self::Arguments> {
        (**self).as_receiver()
    }
}

impl<T> AsReceiver for Ref<T>
where
    T: AsReceiver,
{
    type Arguments = <T as AsReceiver>::Arguments;
    fn as_receiver(&self) -> Receiver<Self::Arguments> {
        (**self).as_receiver()
    }
}

impl<'a, T: CppDeletable> AsReceiver for &'a CppBox<T>
where
    T: AsReceiver,
{
    type Arguments = <T as AsReceiver>::Arguments;
    fn as_receiver(&self) -> Receiver<Self::Arguments> {
        (***self).as_receiver()
    }
}

impl<'a, T: StaticUpcast<QObject> + CppDeletable> AsReceiver for &'a QBox<T>
where
    T: AsReceiver,
{
    type Arguments = <T as AsReceiver>::Arguments;
    fn as_receiver(&self) -> Receiver<Self::Arguments> {
        (***self).as_receiver()
    }
}

impl<'a, T: StaticUpcast<QObject>> AsReceiver for &'a QPtr<T>
where
    T: AsReceiver,
{
    type Arguments = <T as AsReceiver>::Arguments;
    fn as_receiver(&self) -> Receiver<Self::Arguments> {
        (***self).as_receiver()
    }
}

impl<SignalArguments> Signal<SignalArguments> {
    /// Creates a Qt connection between this signal and `receiver`, using the specified
    /// `connection_type`.
    ///
    /// Returns an object that represents the connection. You can use `is_valid()` on this object
    /// to determine if the connection was successful.
    ///
    /// The connection will automatically be deleted when either the sender or the receiver
    /// is deleted.
    ///
    /// # Safety
    ///
    /// The `QObject`s referenced by `self` and `receiver` must be alive.
    ///
    /// [C++ documentation](https://doc.qt.io/qt-5/qobject.html#connect):
    /// <div style='border: 1px solid #5CFF95; background: #D6FFE4; padding: 16px;'>
    /// <p>Creates a connection of the given&nbsp;<em>type</em>&nbsp;from the&nbsp;<em>signal</em>&nbsp;in the&nbsp;<em>sender</em>&nbsp;object to the&nbsp;<em>method</em>&nbsp;in the&nbsp;<em>receiver</em>&nbsp;object. Returns a handle to the connection that can be used to disconnect it later.</p>
    /// <p>You must use the&nbsp;<code>SIGNAL()</code>&nbsp;and&nbsp;<code>SLOT()</code>&nbsp;macros when specifying the&nbsp;<em>signal</em>&nbsp;and the&nbsp;<em>method</em>, for example:</p>
    /// <div>
    /// <pre><a href="https://doc.qt.io/qt-5/qlabel.html">QLabel</a> *label = new <a href="https://doc.qt.io/qt-5/qlabel.html">QLabel</a>;
    /// <a href="https://doc.qt.io/qt-5/qscrollbar.html">QScrollBar</a> *scrollBar = new <a href="https://doc.qt.io/qt-5/qscrollbar.html">QScrollBar</a>;
    /// <a href="https://doc.qt.io/qt-5/qobject.html#QObject">QObject</a>::connect(scrollBar, SIGNAL(valueChanged(int)),
    ///                  label,  SLOT(setNum(int)));</pre>
    /// </div>
    /// <p>This example ensures that the label always displays the current scroll bar value. Note that the signal and slots parameters must not contain any variable names, only the type. E.g. the following would not work and return false:</p>
    /// <div>
    /// <pre>// WRONG
    /// <a href="https://doc.qt.io/qt-5/qobject.html#QObject">QObject</a>::connect(scrollBar, SIGNAL(valueChanged(int value)),
    ///                  label, SLOT(setNum(int value)));</pre>
    /// </div>
    /// <p>A signal can also be connected to another signal:</p>
    /// <div>
    /// <pre>class MyWidget : public <a href="https://doc.qt.io/qt-5/qwidget.html">QWidget</a>
    /// {
    ///     Q_OBJECT
    /// public:
    ///     MyWidget();
    /// signals:
    ///     buttonClicked();
    /// private:
    ///     <a href="https://doc.qt.io/qt-5/qpushbutton.html">QPushButton</a> *myButton;
    /// };
    /// MyWidget::MyWidget()
    /// {
    ///     myButton = new <a href="https://doc.qt.io/qt-5/qpushbutton.html">QPushButton</a>(this);
    ///     connect(myButton, SIGNAL(clicked()),
    ///             this, SIGNAL(buttonClicked()));
    /// }</pre>
    /// </div>
    /// <p>In this example, the&nbsp;<code>MyWidget</code>&nbsp;constructor relays a signal from a private member variable, and makes it available under a name that relates to&nbsp;<code>MyWidget</code>.</p>
    /// <p>A signal can be connected to many slots and signals. Many signals can be connected to one slot.</p>
    /// <p>If a signal is connected to several slots, the slots are activated in the same order in which the connections were made, when the signal is emitted.</p>
    /// <p>The function returns a&nbsp;<a href="https://doc.qt.io/qt-5/qmetaobject-connection.html">QMetaObject::Connection</a>&nbsp;that represents a handle to a connection if it successfully connects the signal to the slot. The connection handle will be invalid if it cannot create the connection, for example, if&nbsp;<a href="https://doc.qt.io/qt-5/qobject.html">QObject</a>&nbsp;is unable to verify the existence of either&nbsp;<em>signal</em>&nbsp;or&nbsp;<em>method</em>, or if their signatures aren't compatible. You can check if the handle is valid by casting it to a bool.</p>
    /// <p>By default, a signal is emitted for every connection you make; two signals are emitted for duplicate connections. You can break all of these connections with a single&nbsp;<a href="https://doc.qt.io/qt-5/qobject.html#disconnect">disconnect</a>() call. If you pass the&nbsp;<a href="https://doc.qt.io/qt-5/qt.html#ConnectionType-enum">Qt::UniqueConnection</a>&nbsp;<em>type</em>, the connection will only be made if it is not a duplicate. If there is already a duplicate (exact same signal to the exact same slot on the same objects), the connection will fail and connect will return an invalid&nbsp;<a href="https://doc.qt.io/qt-5/qmetaobject-connection.html">QMetaObject::Connection</a>.</p>
    /// <p><strong>Note:&nbsp;</strong>Qt::UniqueConnections do not work for lambdas, non-member functions and functors; they only apply to connecting to member functions.</p>
    /// <p>The optional&nbsp;<em>type</em>&nbsp;parameter describes the type of connection to establish. In particular, it determines whether a particular signal is delivered to a slot immediately or queued for delivery at a later time. If the signal is queued, the parameters must be of types that are known to Qt's meta-object system, because Qt needs to copy the arguments to store them in an event behind the scenes. If you try to use a queued connection and get the error message</p>
    /// <div>
    /// <pre><a href="https://doc.qt.io/qt-5/qobject.html#QObject">QObject</a>::connect: Cannot queue arguments of type 'MyType'
    /// (Make sure 'MyType' is registered using <a href="https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1">qRegisterMetaType</a>().)</pre>
    /// </div>
    /// <p>call&nbsp;<a href="https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1">qRegisterMetaType</a>() to register the data type before you establish the connection.</p>
    /// <p><strong>Note:</strong>&nbsp;This function is&nbsp;<a href="https://doc.qt.io/qt-5/threads-reentrancy.html">thread-safe</a>.</p>
    /// <p><strong>See also&nbsp;</strong><a href="https://doc.qt.io/qt-5/qobject.html#disconnect">disconnect</a>(),&nbsp;<a href="https://doc.qt.io/qt-5/qobject.html#sender">sender</a>(),&nbsp;<a href="https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1">qRegisterMetaType</a>(),&nbsp;<a href="https://doc.qt.io/qt-5/qmetatype.html#Q_DECLARE_METATYPE">Q_DECLARE_METATYPE</a>(), and&nbsp;<a href="https://doc.qt.io/qt-5/signalsandslots-syntaxes.html">Differences between String-Based and Functor-Based Connections</a>.</p>
    /// </div>
    pub unsafe fn connect_with_type<R>(
        &self,
        connection_type: ConnectionType,
        receiver: R,
    ) -> CppBox<Connection>
    where
        R: AsReceiver,
        SignalArguments: ArgumentsCompatible<R::Arguments>,
    {
        let receiver = receiver.as_receiver();

        crate::QObject::connect_5a(
            self.0.q_object.as_ptr(),
            self.0.receiver_id.as_ptr(),
            receiver.q_object.as_ptr(),
            receiver.receiver_id.as_ptr(),
            connection_type,
        )
    }

    /// Creates a Qt connection between this signal and `receiver`, using auto connection type.
    ///
    /// See `connect_with_type` for more details.
    pub unsafe fn connect<R>(&self, receiver: R) -> CppBox<Connection>
    where
        R: AsReceiver,
        SignalArguments: ArgumentsCompatible<R::Arguments>,
    {
        self.connect_with_type(ConnectionType::AutoConnection, receiver)
    }
}